Language-Ext Unions - Target runtime doesn't support default interface implementation - language-ext

I'm trying to user the Union type in Language-Ext and am getting error: "Target runtime doesn't support default interface implementation" for the method name and "The type or namespace name 'InconsistentFirstName' could not be found"
My code looks like this:
[Union]
public interface Inconsistency
{
public Inconsistency InconsistentFirstName(string localValue, string contactCentreValue);
public Inconsistency InconsistentSurname(string localValue, string contactCentreValue);
public string GetFieldName(Inconsistency inconsistency)
=> inconsistency switch
{
InconsistentFirstName n => "Name",
InconsistenctSurname s => "Surname",
_ => ""
};
}

GetFieldName needs to be added to a separate class file. There is a partial class that is automatically code generated called InconsistencyCon, so what I was trying to achieve can be done with the following code:
[Union]
public abstract partial class Inconsistency
{
public abstract Inconsistency InconsistentFirstName(string localValue, string remoteValue);
public abstract Inconsistency InconsistentSurname(string localValue, string remoteValue);
}
public static partial class InconsistencyCon
{
public static string FieldName(this Inconsistency inconsistency) =>
inconsistency switch
{
InconsistentFirstName n => "Name",
InconsistentSurname s => "Surname",
_ => ""
};
public static string Test(Inconsistency inconsistency)
{
if (inconsistency is InconsistentFirstName n)
{
var a = n.LocalValue;
var b = n.FieldName;
// etc
}
}
}

Related

JSON Deserialization for polymorphic array in MVC 4 controller

Im using MVC 4 my ActionController recives the following Json:
{
"MainId": 1,
"Actions": [
{
"Attribute1ClassA": 1,
"Attribute2ClassA": 2
},
{
"Attribute1ClassB": 3,
"Attribute2ClassB": 4
},
{
"Attribute1ClassC": 5
}
]
}
and the Controller:
[HttpPost]
public ActionResult Commit(ActionsSummaryViewModel summary)
{
//DO stuff
}
and declaration for classes:
public ActionsSummaryViewModel
{
public int MainId {get;set;}
public IList<MainClass> {get;set;}
}
public class MainClass
{
}
public class ClassA : MainClass
{
public int Attribute1ClassA {get;set;}
public string Attribute2ClassA {get;set;}
}
public class ClassB : MainClass
{
public int Attribute1ClassB {get;set;}
public string Attribute2ClassB {get;set;}
}
public class ClassC : MainClass
{
public int Attribute1ClassC {get;set;}
}
So now, how can i manage the deserialization for the MainClass when the action controller receive the JSON ? because when i call the action the list items are null.
if part of the solution is Json.NET, how i can implement for MVC 4 controllers?
Thanks for your help.
You need a property or set of properties from which you can determine which type the class is to use this method. Using JSON.NET, I deserialize the incoming JSON as a dynamic object, then check the common property, determine the type, and deserialize the value again this type using my model type:
// I'm assuming here you've already got your raw JSON stored in 'value'
// In my implementation I'm using the Web API so I use a media formatter,
// but the same principle could be applied to a model binder or however
// else you want to read the value.
dynamic result = JsonConvert.DeserializeObject(value);
switch ((string)result.type)
{
case "typeone":
return JsonConvert.DeserializeObject<ModelOne>(value);
// ...
default: return null;
}
There's a little bit of extra overhead here because you're deserializing twice, but it's worth it in most cases to me because it's easy to understand what's going on and add new types as needed.
You could parse JSON into dynamic object instead using Json.NET:
using Newtonsoft.Json.Linq:
dynamic data = JObject.Parse("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }");
string name = data.Name;
string address = data.Address.City;

Unable to refactor using LINQ to Entities and LinqKit / PredicateBuilder

I have been trying to refactor a LINQ expression into a method, and have been running into both the "Internal .NET Framework Data Provider error 1025." and "The parameter 'xyz' was not bound in the specified LINQ to Entities query expression." exceptions.
Here are the relevant parts of the entity model (using EF 4.2 / LINQ to Entities):
public class Place : Entity
{
public string OfficialName { get; protected internal set; }
public virtual ICollection<PlaceName> { get; protected internal set; }
}
public class PlaceName : Entity
{
public string Text { get; protected internal set; }
public string AsciiEquivalent { get; protected internal set; }
public virtual Language TranslationTo { get; protected internal set; }
}
public class Language : Entity
{
public string TwoLetterIsoCode { get; protected internal set; }
}
The basic relational model is this:
Place (1) <-----> (0..*) PlaceName (0..*) <-----> (0..1) Language
I am trying to create a query which will, when given a search term, try to find Place entities whose OfficialName starts with the term OR who has a PlaceName whose Text or AsciiEquivalent starts with the search term. (Language isn't where I'm having trouble, though it is part of the query, because PlaceNames should only match for the CultureInfo.CurrentUICulture.TwoLetterIsoLanguageName.)
The following code does work:
internal static IQueryable<Place> WithName(this IQueryable<Place> queryable,
string term)
{
var matchesName = OfficialNameMatches(term)
.Or(NonOfficialNameMatches(term));
return queryable.AsExpandable().Where(matchesName);
}
private static Expression<Func<Place, bool>> OfficialNameMatches(string term)
{
return place => place.OfficialName.StartsWith(term);
}
private static Expression<Func<Place, bool>> NonOfficialNameMatches(string term)
{
var currentLanguage = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;
return place => place.Names.Any(
name =>
name.TranslationToLanguage != null
&&
name.TranslationToLanguage.TwoLetterIsoCode == currentLanguage
&&
(
name.Text.StartsWith(term)
||
(
name.AsciiEquivalent != null
&&
name.AsciiEquivalent.StartsWith(term)
)
)
);
}
What I am trying to do next is refactor the NonOfficialNameMatches method to extract the name => ... expression out into a separate method, so that it can be reused by other queries. Here is one example I have tried, which does not work and throws the exception "The parameter 'place' was not bound in the specified LINQ to Entities query expression.":
private static Expression<Func<Place, bool>> NonOfficialNameMatches(string term)
{
return place => place.Names.AsQueryable().AsExpandable()
.Any(PlaceNameMatches(term));
}
public static Expression<Func<PlaceName, bool>> PlaceNameMatches(string term)
{
var currentLanguage = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;
return name =>
name.TranslationToLanguage != null
&&
name.TranslationToLanguage.TwoLetterIsoCode == currentLanguage
&&
(
name.Text.StartsWith(term)
||
(
name.AsciiEquivalent != null
&&
name.AsciiEquivalent.StartsWith(term)
)
);
}
When I don't have the .AsExpandable() chain in NonOfficialNameMatches, then I get the "Internal .NET Framework Data Provider error 1025." exception.
I have followed other advice here such as several combinations of invoking .Expand() on the predicates, but always end up with one of the aforementioned exceptions.
Is it even possible to factor out this expression into a separate method using LINQ to Entities with LinqKit / PredicateBuilder? If so, how? What am I doing wrong?
The method below should work:
private static Expression<Func<Place, bool>> NonOfficialNameMatches(string term)
{
Expression<Func<PlaceName, bool>> placeNameExpr = PlaceNameMatches(term);
Expression<Func<Place, bool>> placeExpr =
place => place.Names.Any(name => placeNameExpr.Invoke(name));
return placeExpr.Expand();
}
EDIT: Adding additional explanations
The PlaceNameMatches method works as you wrote it. Your issues were in how you used the method. If you want to factor out parts of an expression follow the 3 steps I did in the method above.
Set a local variable to the expression created by a method.
Set another local variable to a new expression that Invokes the local variable expression.
Call the LinkKit Expand method: this will expand any Invoked expressions

Automapper error on simple conversion without CreateMap

I have these 2 models:
public class SiteSettingsViewModel
{
public decimal SubscriptionFee { get; set; }
}
public class SiteSettings
{
public decimal SubscriptionFee { get; set; }
}
and the code:
var model = Mapper.Map<SiteSettings, SiteSettingsViewModel>(settingService.GetSettings());
Which throws the error of:
Trying to map WebApp1.Domain.Site.SiteSettings to WebApp1.WebUI.ViewModel.SiteSettingsViewModel.
Missing type map configuration or unsupported mapping.
Exception of type 'AutoMapper.AutoMapperMappingException' was thrown.
Why do I need to put the code:
Mapper.CreateMap<SiteSettings, SiteSettingsViewModel>();
To me, this seems like I'm writing monkey code. This is not needed.
Why didn't the 1 line work?
One reason is that it's useful for more complex mapping scenarios where you need to define more specific behaviors. For example (from CodePlex):
Mapper.CreateMap<CalendarEvent, CalendarEventForm>()
.ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.EventDate.Date))
.ForMember(dest => dest.EventHour, opt => opt.MapFrom(src => src.EventDate.Hour))
.ForMember(dest => dest.EventMinute, opt => opt.MapFrom(src => src.EventDate.Minute));
Another option for simple mapping like what you're doing is to make a generic mapper that takes care of the CreateMap call for you, like this:
public interface IMapper<T1, T2>
{
T1 Map(T2 source);
}
public class Mapper<T1, T2> : IMapper<T1, T2> where T1 : class where T2 : class
{
public Mapper()
{
Mapper.CreateMap<T2, T1>();
}
public T1 Map(T2 source)
{
return Mapper.Map<T2, T1>(source);
}
}
And then you can just instantiate them directly like:
var mapper = new Mapper<SiteSettings, SiteSettingsViewModel>();
Or register them to be injected into your controllers, or wherever else you plan on using them. Hope that helps.

EF 4: Referencing Non-Scalar Variables Not Supported

I'm using code first and trying to do a simple query, on a List property to see if it contains a string in the filtering list. However I am running into problems. For simplicity assume the following.
public class Person
{
public List<string> FavoriteColors { get; set; }
}
//Now some code. Create and add to DbContext
var person = new Person{ FavoriteColors = new List<string>{ "Green", "Blue"} };
dbContext.Persons.Add(person);
myDataBaseContext.SaveChanges();
//Build
var filterBy = new List<string>{ "Purple", "Green" };
var matches = dbContext.Persons.AsQueryable();
matches = from p in matches
from color in p.FavoriteColors
where filterBy.Contains(color)
select p;
The option I am considering is transforming this to a json serialized string since I can perform a Contains call if FavoriteColors is a string. Alternatively, I can go overboard and create a "Color" entity but thats fairly heavy weight. Unfortunately enums are also not supported.
I think the problem is not the collection, but the reference to matches.
var matches = dbContext.Persons.AsQueryable();
matches = from p in matches
from color in p.FavoriteColors
where filterBy.Contains(color)
select p;
If you check out the Known Issues and Considerations for EF4 this is more or less exactly the case mentioned.
Referencing a non-scalar variables,
such as an entity, in a query is not
supported. When such a query executes,
a NotSupportedException exception is
thrown with a message that states
"Unable to create a constant value of
type EntityType.
Also note that it specifically says that referencing a collection of scalar variables is supported (that's new in EF 4 imo).
Having said that the following should work (can't try it out right now):
matches = from p in dbContext.Persons
from color in p.FavoriteColors
where filterBy.Contains(color)
select p;
I decided to experiment by creating a "StringEntity" class to overcome this limitation, and used implicit operators to make nice easy transformations to and from strings. See below for solution:
public class MyClass
{
[Key, DatabaseGenerated(DatabaseGenerationOption.Identity)]
public Guid Id { get; set; }
public List<StringEntity> Animals { get; set; }
public MyClass()
{
List<StringEntity> Animals = List<StringEntity>();
}
}
public class StringEntity
{
[Key, DatabaseGenerated(DatabaseGenerationOption.Identity)]
public Guid Id { get; set; }
public string Value { get; set; }
public StringEntity(string value) { Value = value; }
public static implicit operator string(StringEntity se) { return se.Value; }
public static implicit operator StringEntity(string value) { return new StringEntity(value); }
}
public class MyDbContext : DbContext
{
public DbSet<MyClass> MyClasses { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MyClass>()
.HasMany(x => x.Animals)
.WithMany()
.Map(x =>
{
x.MapLeftKey(l => l.Id, "MyClassId");
x.MapRightKey(r => r.Id, "StringEntityId");
});
}
}
...Everything looked like it was working perfectly with some testing(Albeit heavy), and then I implemented for its original purpose, a Multiselect ListBox in an MVC3 view. For reasons unknown to me, IF the ListBox is assigned the same NAME as an Entity Collection Property, none of your selected items will be loaded.
To demonstrate the following did NOT work:
//Razor View Code
string[] animalOptions = new string[] {"Dog", "Cat", "Goat"};
string[] animalSelections = new string[] {"Dog", "Cat"};
Html.ListBox("Animals", Multiselect(animalOptions, animalSelections));
To get around this limitation, I needed to do four things:
//#1 Unpluralize the ListBox name so that is doesn't match the name Model.Animals
var animalOptions = new string[] {"Dog", "Cat", "Goat"};
#Html.ListBox("Animal", new MultiSelectList(animalOptions, Model.Animals.Select(x => x.Value)))
//#2 Use JQuery to replace the id and name attribute, so that binding can occur on the form post
<script type="text/javascript">
jQuery(function ($) {
$("select#Animal").attr("name", "Animals").attr("id", "Animals");
});
</script>
//#3 Create a model binder class to handle List<StringEntity> objects
public class StringEntityListBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var stringArray = controllerContext.HttpContext.Request.Params.GetValues(bindingContext.ModelName);
return stringArray.Select(x => new StringEntity(x)).ToList();
}
}
//#4 Initialize the binder in your Global.asax setup.
ModelBinders.Binders.Add(typeof(List<StringEntity>), new StringEntityListBinder ());
Note, that the Listbox bug did NOT occur when the property was a List of strings, it just didn't like it when it was a List of entities.

StructureMap Exception Code 205 Missing requested Instance property

I'm trying out the latest build of StructureMap, to learn about IoC containers and the like. As my first test, I have the following class:
public class Hospital
{
private Person Person { get; set; }
private int Level { get; set; }
public Hospital(Person employee, int level)
{
Person = employee;
Level = level;
}
public void ShowId()
{
Console.WriteLine(this.Level);
this.Person.Identify();
}
}
I then use StructureMap like this:
static void Main()
{
ObjectFactory.Configure(x =>
{
x.For<Person>().Use<Doctor>();
x.ForConcreteType<Hospital>().Configure.Ctor<int>().Equals(23);
});
var h = ObjectFactory.GetInstance<Hospital>();
h.ShowId();
}
So I'm passing a Doctor object as the first constructor param to Hospital, and I'm trying to set the level param to 23. When I run the above code I get:
Unhandled Exception:
StructureMap.StructureMapException:
StructureMap Exception Code: 205
Missing requested Instance property
"level" for InstanceKey
"5f8c4b74-a398-43f7-
91d5-cfefcdf120cf"
So it looks like I'm not setting the level param at all. Can someone point me in the right direction - how do I set the level param in the constructor?
Cheers.
Jas.
You were very close. You accidentially used the System.Object.Equals method on the dependency expression rather than the Is Method. I would also recommend when configuring common types like string or value types (int, DateTime) to specify the constructor argument name to avoid ambiguity.
Here is my test with what you are looking for:
[TestFixture]
public class configuring_concrete_types
{
[Test]
public void should_set_the_configured_ctor_value_type()
{
const int level = 23;
var container = new Container(x =>
{
x.For<Person>().Use<Doctor>();
x.ForConcreteType<Hospital>().Configure.Ctor<int>("level").Is(level);
});
var hospital = container.GetInstance<Hospital>();
hospital.Level.ShouldEqual(level);
}
}

Resources