question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

String properties behave differently in a request

See original GitHub issue

While making my own SpecimenBuilder, I stumbled upon an issue, I don’t know how to fix - basically string properties cannot be cast to PropertyInfo, so I cannot access CustomAttributes and the class they are declared in.

Here is some code I made for this example:

    [Table("ItemTable", Schema = "prd")]
    public class Item : IEntity
    {
        [Column(@"Id", Order = 1, TypeName = "bigint")]
        [Display(Name = "Item id")]
        public int Id { get; set; }

        [Column(@"Name", Order = 2, TypeName = "nvarchar")]
        [MaxLength(5)]
        [StringLength(5)]
        [Display(Name = "Item name")]
        public string Name { get; set; }

        [Column(@"Effect", Order = 3, TypeName = "nvarchar")]
        [MaxLength(6)]
        [StringLength(6)]
        [Display(Name = "Item effect")]
        public string Effect { get; set; }

        [Column(@"LastUpdated", Order = 4, TypeName = "datetime")]
        [Display(Name = "Last Updated")]
        public System.DateTime? LastUpdated { get; set; }
    }

The first thing I do in my CustomSpecimen, is cast request to PropertyInfo, that works wonderful for all members of this class, except for strings.

        public object Create(object request, ISpecimenContext context)
        {
            var property = request as PropertyInfo;
            if (property == null) return new NoSpecimen();

            ColumnAttribute column = property.GetCustomAttribute<ColumnAttribute>();
            TableAttribute table = property.ReflectedType.GetCustomAttribute<TableAttribute>();
            
            ...
        }

The above code casts the Id and LastUpdated to properties and retrieves their custom attributes without any issues, however, when it comes to Name and Effect properties, the only request that ever comes in, is either of type string (TypeInfo not PropertyInfo), or it is a Ranged string request.

Then, I thought I will be clever, and I will only take the class itself in the request and resolve the properties in a single call to my CustomSpecimen, like this:

        public object Create(object request, ISpecimenContext context)
        {
            var typeInfo = request as TypeInfo;
            if (typeInfo == null) return new NoSpecimen();
            if (!typeInfo.ImplementedInterfaces.Contains(typeof(IEntity))) return new NoSpecimen();

            // Stores the class name, local offsets and other things for incrementing unique IDs
            PrepareForNextDataSet(typeInfo.FullName);
            TableAttribute table = typeInfo.GetCustomAttribute<TableAttribute>();

            var instance = Activator.CreateInstance(typeInfo.AsType());
            foreach (var prop in typeInfo.DeclaredProperties)
            {
                ColumnAttribute column = prop.GetCustomAttribute<ColumnAttribute>();
                // Tries to get info about this column from metadata
                UniqueKeyModel uniqueKeyModel = GetUniqueKeyModel(table.Name, column.Name);

                object resolved;
                // If this column is part of unique keys, then assign a unique id
                if (uniqueKeyModel?.UniqueKeyOrder != null)
                {
                    // try assigning unique id, if not possible then resolve from context
                    resolved = GetValueForProperty(prop) ?? context.Resolve(prop);
                }
                else
                {
                    // if field is not part of UniqueKeys - then randomize output
                    resolved = context.Resolve(prop);
                }
                prop.SetValue(instance, resolved, null);

            }

            return instance;
        }

Now, this approach works perfectly until I start to customize specific values using ICustomization interface or simple calls to Fixture.Build(), like this:

    public class ItemCustomization : ICustomization
    {
        /// <inheritdoc />
        public void Customize(IFixture fixture)
        {
            fixture.Customize<Item>(c =>
                c.With(p => p.Name, "example"));
        }
    }  

And final usage:

    Fixture fixture = new Fixture();
    fixture.Customizations.Add(new CustomSpecimen());
    fixture.Customize(new ItemCustomization());
    var result = fixture.CreateMany<Item>(5).ToList();

My specimen is no longer working, because customization has overriden specific values and the Item class does not get sent to my CustomSpecimen, only the remaining fields as primitive data types.

Please suggest a better way of doing this - in essence, what I am trying to accomplish is to have a CustomSpecimen that would populate fields for all types of entity framework generated classes. (I cannot use the default builders, because I have custom logic based on attributes).
CustomSpecimen validates Column name and Table name attributes for each property against a list, and if that list contains an entry for this column & table, then it will assign a unique id. Otherwise, it will resolve the property from context.
Then it should check, if any of the fields have been overriden with customization, if yes, then it should use that specific value instead.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:8 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
janissimsonscommented, Oct 16, 2018

Yes, it does, thank you.

0reactions
zvirjacommented, Oct 16, 2018

@janissimsons Great, you are welcome 😉 Sorry again for the long response time - hope it was still in time.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Why can't I add properties to a string object in javascript?
It looks like an object (which you can add random properties to) because you can call methods on it: test.includes(searchString).
Read more >
Best Practices for Comparing Strings in .NET
Learn how to compare strings effectively in .NET applications.
Read more >
Use empty string, null or remove empty property in API ...
Code behaves differently. Then in addition to that - it is up to you to behave consistently, and possibly adopt some of your...
Read more >
Useful string methods - Learn web development | MDN
Finding the length of a string. This is easy — you use the length property. Try entering the following lines: js
Read more >
JavaScript type confusion: Bypassed input validation (and ...
Some built-in methods defined on both String and Array types (like indexOf or includes ) could behave differently depending on the type of...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found