Update 8:
The question has a new title ,hopefully it will help other avoid time consuming bugs...
I have the following code:
(You need a reference to System.ComponentModel.DataAnnotations)
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var itemModelX = new MyModel() { Name = "1<d" };
var contextX = new ValidationContext(itemModelX, serviceProvider: null, items: null);
var resultsX = new List<ValidationResult>();
var isValidX = Validator.TryValidateObject(itemModelX, contextX, resultsX, true);
if (isValidX)
Console.WriteLine("Should not see this");
Console.WriteLine("Finished");
Console.ReadLine();
}
}
public class MyModel
{
[MultipleRegExAttribute2(#"[^?.]{1,100}$")]
[MultipleRegExAttribute2(#"^((?![><&])[\s\S])*$")]
public string Name { get; set; }
}
[System.AttributeUsage(System.AttributeTargets.Field | System.AttributeTargets.Property, AllowMultiple = true)]
public class MultipleRegExAttribute2 : RegularExpressionAttribute
{
public MultipleRegExAttribute2(string pattern) : base(pattern) { }
}
}
In Visual Studio 2013 Premium Update 5 the output is
"Should not see this"
"Finished"
In Visual Studio 2015 Enterprise Update 1 the output is
"Finished"
Roslyn is the correct one,the question is why 2013 is not working?
I'm pretty sure is that was working in 2013 also,don't know when it broke,I don't have a previous update to test it...
I'm using .NET 4.5.1
Update 1
Even if I remove the reference to System.ComponentModel.DataAnnotations and add the code to my project (You can find the code here) I get different outputs.
Update 2
Forgot to say that it only happens if I have 2 MultipleRegExAttributes ,if I remove the first one it works as expected
Update 3
I've uploaded an entire solution here
Update 4
I check the generated IL for both assemblies but the only difference I see is the initialization of a few locals
VS2015
.locals init (
[0] class [System.ComponentModel.DataAnnotations]System.ComponentModel.DataAnnotations.ValidationContext contextX,
[1] class [mscorlib]System.Collections.Generic.List`1<class [System.ComponentModel.DataAnnotations]System.ComponentModel.DataAnnotations.ValidationResult> resultsX )
VS2013
.locals init (
[0] class ConsoleApp.MyModel itemModelX,
[1] class [System.ComponentModel.DataAnnotations]System.ComponentModel.DataAnnotations.ValidationContext contextX,
[2] class [mscorlib]System.Collections.Generic.List`1<class [System.ComponentModel.DataAnnotations]System.ComponentModel.DataAnnotations.ValidationResult> resultsX,
[3] bool isValidX,
[4] class ConsoleApp.MyModel '<>g__initLocal0'
)
Update 5
Almost there...
VS2015 only checks the second attribute(Which happens to be the one I
care about in the case)
VS2013 only checks the first argument...
Update 6
After many many hours...
- It seems there always is a problem if I have more than 1 RegularExpressionAttribute
That could be my mistake because I extend the class and "override" AllowMultiple
2.Why do I get different results when I compile it with VS2015?
The class that returns the attributes is System.ComponentModel.TypeDescriptor in method GetAttributes.
I have to see what is change there...
Update 7
It seems that something has change in way PropertyDescriptor/MemberDescriptor returns the Attributes.
In VS2015 it prints the second regex,in VS2013 the first.
So,which of the following is true?
a)This is an implementation detail and I shouldn't rely on this
b)This is a bug since it is a breaking change
c)a and b
d)other
.
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var property = TypeDescriptor.GetProperties(typeof(MyModel))[0];
var attribute = property.Attributes.Cast<Attribute>();
foreach (var item in attribute)
if (item is MultipleRegExAttribute2)
Console.WriteLine(((MultipleRegExAttribute2)item).GetPattern());
Console.ReadLine();
}
}
public class MyModel
{
[MultipleRegExAttribute2(#"[^?.]{1,100}$")]
[MultipleRegExAttribute2(#"^((?![><&])[\s\S])*$")]
public string Name { get; set; }
}
[System.AttributeUsage(System.AttributeTargets.Field | System.AttributeTargets.Property, AllowMultiple = true)]
public class MultipleRegExAttribute2 : RegularExpressionAttribute
{
public MultipleRegExAttribute2(string pattern) : base(pattern) { }
public string GetPattern() { return this.Pattern; }
}
}
DO NOT extend RegularExpressionAttribute and set AllowMultiple to true
It will bring you nothing but trouble.
You can create 2 distinct attributes that inherit from RegularExpressionAttribute.
public class MyModel
{
[MultipleRegExAttribute2(#"[^?.]{1,100}$")]
[MultipleRegExAttribute3(#"^((?![><&])[\s\S])*$")]
public string Name { get; set; }
}
public class MultipleRegExAttribute2 : RegularExpressionAttribute
{
public MultipleRegExAttribute2(string pattern) : base(pattern) { }
}
public class MultipleRegExAttribute3 : RegularExpressionAttribute
{
public MultipleRegExAttribute3(string pattern) : base(pattern) { }
}
Update
A friend of mine showed me the root of the problem.
I have to override the TypeId property.
See this question:Custom validation attribute with multiple instances problem
and this article :The Importance of TypeId in ASP.NET MVC DataAnnotations Validation Attributes
Related
I have this test class:
using NSubstitute;
using NUnit.Framework;
using System;
using System.Linq.Expressions;
namespace MyTests
{
public class Tests
{
[Test]
public void Test()
{
var companyBL = Substitute.For<ICompanyBL>();
companyBL.GetCompany(c => new { c.RegionID }).ReturnsForAnyArgs(new
{
RegionID = 4,
});
var company = companyBL.GetCompany(c => new { c.RegionID });
var dataRetriever = new DataRetriever(companyBL);
}
}
}
and this code in another project:
namespace MyTests
{
using System;
using System.Linq.Expressions;
public interface ICompanyBL
{
T GetCompany<T>(Expression<Func<Company, T>> selector);
}
public partial class Company
{
public int RegionID { get; set; }
}
public class DataRetriever
{
public DataRetriever(ICompanyBL companyBL)
{
//This is null:
var company = companyBL.GetCompany(c => new
{
c.RegionID
});
}
}
}
The company var is null.
However, when the code is all contained in the same .cs file in the same project, the value is not null.
Why is the value null when used in another file in another project?
NSubstitute version = 1.10.0.0.
.NET Framework version = 4.5.2.
Issue submitted in Github:
https://github.com/nsubstitute/NSubstitute/issues/598
I'm not sure how nsubstitute works but I believe you are running into issues where anonymous types only match inside single assembly, they are always different across assemblies.
Roughly you are mocking companyBL.GetCompany<TestAssembly.AnonymousTypeForRegionID> and your tested code calls companyBL.GetCompany<ProductAssembly.AnonymousTypeForRegionID>.
The easiest fix - use some type to pass data shared between those two assemblies. Even Tuple would do. More ideas in Return/consume dynamic anonymous type across assembly boundaries
I'm using Entity Framework and .Net Core 2.0 for the first time (I'm also pretty new to C#, but I've been using the traditional .Net Framework & VB since version 1... so I'm no newbie to .Net development), and I've already run into a problem creating my database.
Take this simple scenario: I want to store some information about some electric pumps. Two of the properties are a min/max type range, so I've implemented these as a simple class, thus:
public class Pump
{
[Key]
public int pumpId { get; set; }
public string pumpName { get; set; }
public int pumpControlChannel { get; set; }
public MinMax normalCurrent { get; set; }
public MinMax normalFlowRate { get; set; }
}
[ComplexType]
public class MinMax
{
public int min { get; set; }
public int max { get; set; }
}
As you can see, I've tried the [ComplexType] decorator, to no avail.
Anyway, now create a dead simple DBContext class to manage my Pumps class. I'm using Sqlite:
public class EFDB : DbContext
{
public DbSet<Pump> pumps { get; private set; }
private static DbContextOptions GetOptions(string connectionString)
{
var modelBuilder = new DbContextOptionsBuilder();
return modelBuilder.UseSqlite(connectionString).Options;
}
public EFDB(string connectionString) : base(GetOptions(connectionString)) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
try
{
// modelBuilder.ComplexType<MinMax>(); // ComplexType not recognised
base.OnModelCreating(modelBuilder);
}
catch (Exception ex)
{
System.Diagnostics.Debugger.Break();
}
}
}
and lastly a simple static class to call it (I embeded it in a bigger program... to duplicate this problem you could just stick the code lines into program.cs):
public static class TryMe
{
public static void MakeMeFail()
{
using (var db = new EFDB("FileName=C:\\temp\\test_effail.db"))
{
try
{
db.Database.EnsureCreated();
}
catch (Exception ex)
{
System.Diagnostics.Debugger.Break(); // If we hit this line, it fell over
}
}
System.Diagnostics.Debugger.Break(); // If we hit this line, it worked.
}
}
Just call TryMe.MakeMeFail(), the code fails at db.Database.EnsureCreated().
From everything I've read, [ComplexType] should do what I want... but it Just Doesn't. Nor can I find modelBuilder.ComplexType<T> anywhere.
It may just be a library reference I'm missing...? The above code uses the following:
using System;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
However, NONE of the documentation/examples I can find anywhere show which libraries need referencing!
Thanks in advance.
[PS: Apologies to those who already saw this question, I'm using EF Core 2.0, NOT EF6]
Typical... it's always the way, isn't it? 5 minutes after posting, you discover the answer to your own question....
The answer, in this case, can be found here:
https://learn.microsoft.com/en-us/ef/core/modeling/owned-entities
EF Core calls this sort of entity an "owned" entity, rather than a "complex type".
Simply adding these lines to `OnModelCreating' fixed the issue:
modelBuilder.Entity<Pump>().OwnsOne(p => p.normalCurrent);
modelBuilder.Entity<Pump>().OwnsOne(p => p.normalFlowRate);
The database now creates (correctly, I think, I haven't verified that yet).
I use Dapper and TableAttribute:
using Dapper.Contrib.Extensions;
namespace MyCompany.Entities
{
[Table(Config.TABLE_ARCHIVO_CLIENTE)]
public partial class ArchivoCliente
{
Working
public const string TABLE_ARCHIVO_CLIENTE = "Archivo_Cliente";
Not working if not const string. I try use a static property for use appSettings:
public static string TABLE_ARCHIVO_CLIENTE
{
get
{
return ConfigurationManager.AppSettings.Get(KeyTable);
}
}
Any suggestions for using AppSettings ?
Attribute parameters require constants.
Checking the Dapper.Contrib code, it appears very unusually to access the attribute by name. If it was by type, you could do something like:
class ConfigTableAttribute : TableAttribute {
public ConfigTableAttribute(string configSetting)
: base(LookupTableNameFromConfig(configSetting));
private static string LookupTableNameFromConfig(string configSetting)
{
// TODO: your code here
}
}
and annotate your code with:
[ConfigTable(nameof(Config.TABLE_ARCHIVO_CLIENTE))]
class Foo {}
It would then be your job to implement the TODO which would fetch the actual value via reflection or an indexer, etc. In the code shown, the input configSetting would be TABLE_ARCHIVO_CLIENTE.
However, since it accesses it by name and dynamic, all you actually need is something called TableAttribute that has a Name. You could do the same thing as above, but in a different namespace:
namespace MyEvilness {
class TableAttribute : Attribute {
public TableAttribute(string configSetting) {
Name = LookupTableNameFromConfig(configSetting);
}
// etc as before
}
}
and use:
[MyEvilness.Table(nameof(Config.TABLE_ARCHIVO_CLIENTE))]
class Foo {}
Word of caution; I consider the current implementation to be a bug! I understand why it is done that way (i.e. so it works with EF), but I'm tempted to make it work for either approach.
I've written a small package to overcome this issue. It assigns the value as tablename if the key matches to FullName, in the configuration file. With some effort spend to avoid sql injection.
One can add it like dependency injection
// Startup.cs or Program.cs
// ...
services.ReadTablenamesFromConfig(configuration.GetSection("MySectionName"));
// ...
with configuration:
// appsettings.json
...
"MySectionName": {
"TableNames": {
"Demo.Sale": "sale_2020"
}
},
...
For the model:
// Sale.cs
namespace Demo
{
//[Table("sale_2020")]
public class Sale
{
public string Product { get; set; }
public int Quantity { get; set; }
}
}
See a better example here.
As for the time being, the implementation is as follows:
// TablenameExtensions.cs
using Dapper.Contrib.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
namespace Dapper.Contrib.Extensions.Tablename
{
public static class TablenameExtensions
{
private static TablenameConfig _config;
public static IServiceCollection ReadTablenamesFromConfig(this IServiceCollection services, IConfigurationSection configSection)
{
services.Configure<TablenameConfig>(configSection);
_config = configSection.Get<TablenameConfig>();
SqlMapperExtensions.TableNameMapper = TableName;
return services;
}
private static string TableName(Type type) => _config.TableNames[type.FullName].Replace("`", "");
public static string TableName<T>() => TableName(typeof(T));
}
}
where:
// TablenameConfig.cs
using System.Collections.Generic;
namespace Dapper.Contrib.Extensions.Tablename
{
internal class TablenameConfig
{
public IDictionary<string, string> TableNames { get; set; }
}
}
I have three solution A, B and C.
I have an enum in A like this:
using System.Runtime.Serialization;
namespace A.Entities
{
[DataContract]
public enum Status
{
[DataMember]
Active = 0,
[DataMember]
Inactive = 1,
}
}
I reference it in solution B like this:
using A.Entities;
namespace B.Entities
{
public class User
{
[DataMember]
public string UserName { get; set; }
[DataMember]
public Status Status { get; set; }
}
}
In solution C, I use them like this:
using B.Entities;
using Status = A.Entities.Status;
namespace C.TestDatas
{
public class UserTestData
{
public static User CreateUser()
{
return new User
{
Status = Status.Active,
}
};
}
}
When I invoke User method, it throw exception:
Method not found: 'Void B.Entities.User.set_Status(A.Entities.Status)'.
Why? I hope someone can help me, thanks!
I can't reproduce the problem. Your code works fine on my computer (on .NET framework 4.5)
Could you make sure that you project is configured correctly:
Both A and B solutions have reference to System.Runtime.Serialization
C has reference to A and B. Make sure you don't link the dll from /Bin folder, but you have the project reference
Hit Clean and Rebuild buttons in Visual Studio.
I wrote simple piece of code to get involved into Afterthought, but it doesn't work and I've got no idea why. A huge part of it is taken from other SO question: How to implement simple Property Ammendment with Afterthought.
using System;
using System.Collections.Generic;
using Afterthought;
namespace SecondAmendmentTest
{
class Program
{
public class TestUser
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool HasChanged { get; set; }
public void method()
{
Console.WriteLine("method");
}
}
public class TestUserAmmendment<T> : Amendment<T, T> where T : TestUser
{
public TestUserAmmendment()
{
Properties
.AfterSet((instance, x, y, z, a) => instance.HasChanged = true);
Methods.After(ExceptionMethod);
}
private object ExceptionMethod(T instance, string method, object[] parameters, object result)
{
throw new NotImplementedException();
}
}
static void Main(string[] args)
{
TestUser tu = new TestUser();
Console.WriteLine("Has changed: " + tu.HasChanged.ToString());
Console.WriteLine("Performing changes...");
tu.Id = 5;
tu.FirstName = "name";
tu.LastName = "lastname";
Console.WriteLine("Has changed: " + tu.HasChanged.ToString());
tu.method();
Console.ReadKey();
}
}
}
It compiles, but no changes are made into output exe file. I've configured post build event. Build output:
1>------ Rebuild All started: Project: SecondAmendmentTest, Configuration: Debug Any CPU ------
1> SecondAmendmentTest -> C:\Users\Lukasz\Documents\Visual Studio 11\Projects\SecondAmendmentTest\SecondAmendmentTest\bin\Debug\SecondAmendmentTest.exe
1> Amending SecondAmendmentTest.exe (5,559 seconds)
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========
And finally output from application after running:
C:\Users\Lukasz\Documents\Visual Studio 11\Projects\SecondAmendmentTest\SecondAmendmentTest\bin\Debug>SecondAmendmentTest.exe
Has changed: False
Performing changes...
Has changed: False
method
Neither HasChanged property were modified nor NotImplementedException was thrown. I'm using last sources from Git. Have you some ideas why it doesn't work?
EDIT: Here is entire solution: SecondAmendmentTest.zip
I know it's a year later but:
To build on Willem van Ketwich's answer, there was actually a bug (oversight?) in Afterthought.NET that wasn't allowing nested types to be amended if thier parent type wasn't amended.
I've submitted a pull request fixing the bug.
In addition to this fix, you still need the [Amendment(typeof(TestUserAmmendment<>))] on your nested class to trigger the amendment.
I got this to work by doing two things:
Move the TestUser and TestUserAmmendment classes out of the Program
class so they aren't nested.
Add the attribute [Amendment(typeof(TestUserAmmendment<>))] to the
TestUser class.
The working solution can be found here.