How to: StructureMap and configuration based on runtime parameters? - configuration

In a nutshell - I want to be able to instantiate object based on runtime parameters. In this particular case there are only two parameters but the problem is that I'm facing different permutations of these parameters and it gets messy.
Here is the situation: I want to get an instance of an object specific to, say, given country and then, say, specific state/province. So, considering the US, there are 50 possible combinations. In reality it's less than that but that's the max. Think of it this way, I want to find out what's the penalty for smoking pot in a given country/state, I pass this information in and I get instantiated object telling me what it is.
To the code (for reference only):
interface IState
{
string Penalty { get; }
}
interface ICountry
{
IState State { get; set; }
string Name { get; }
}
class BasePenalty : IState
{
virtual public string Penalty { get { return "Slap on a wrist"; } }
}
class USA : ICountry
{
public USA(IState state)
{
State = state;
}
public IState State { get; set; }
public string Name { get { return "USA"; } }
}
class Florida: BasePenalty
{
public override string Penalty { get { return "Public beheading"; } }
}
// and so on ... I defined other states
// which have penalties other than the "Slap on a wrist"
How do I configure my container that when given country and state combination it will return the penalty? I tried combinations of profile and contextual binding but that configuration was directly proportional to the number of classes I've created. I have already gone thru trouble of defining different combinations. I'd like to avoid having to do the same during container configuration. I want to inject State into the Country. Also, I'd like to return UsaBasePenalty value in case state is not specified. Is that possible? Perhaps these is something wrong with the design.

I usually use a Factory for this type of problem. I would first remove the IState/ICountry interfaces and replace them with a more specific IPenaltyCalculator. Have the factory take in ALL instances (IEnumerable<IPenaltyCalculator> in the factory's constructor). Then you ask the factory for the correct instance by passing in the contextual information (current state, country) as parameters. The IPenaltyCalculator has an additional method: bool AppliesTo(string state, string country). The factory loops over them all and returns the first one that returns true from AppliesTo. If none return true, return your DefaultPenaltyCalculator.

Related

EF Core 7 can't deserialize dynamic-members in JSON column

I am trying to map my Name column to a dynamic object. This is how the raw JSON data looks (note that this is SQL-morphed from our old relational data and I am not able to generate or interact with this column via EF Core):
{ "en": "Water", "fa": "آب", "ja": "水", ... }
Just to note, available languages are stored in a separate table and thus are dynamically defined.
Through T-SQL I can perfectly interact with these objects eg
SELECT *
FROM [MyObjects]
WHERE JSON_VALUE(Name, '$.' + #languageCode) = #searchQuery
But it seems EF Core doesn't want to even deserialize these objects as whole, let alone query them.
What I get in a simple GetAll query is an empty Name. Other columns are not affected though.
I have tried so far
Using an empty class with a [JsonExtensionData] dictionary inside
Using a : DynamicObject inheritance and implementing GetDynamicMembers, TryGetMember, TrySetMember, TryCreateInstance
Directly mapping to a string dictionary.
Combining 1 & 2 and adding an indexer operator on top.
All yield the same results: an empty Name.
I have other options like going back to a junction table relational which I have many issues with, hardcoding languages which is not really intuitive and might cause problems in the future, using HasJsonConversion which basically destroys the performance on any search action... so I'm basically stuck here with this.
I think currently it's not fully supported:
You can not use dynamic operations on an expression tree like a Select statement because it needs to be translated.
JsonValue and JsonQuery requires a path to be resolved.
If you specify OwnsOne(entity = >entity.owned, owned => owned.ToJson()) and the Json could not be parsed you will get an error.
I suggest this workaround while the EF team improves the functionality.
Create a static class with static methods to be used as decoys in the expression tree. This will be mapped to the server built-in functions.
public static class DBF
{
public static string JsonValue(this string column, [NotParameterized] string path)
=> throw new NotSupportedException();
public static string JsonQuery(this string column, [NotParameterized] string path) => throw new NotSupportedException();
}
Include the database functions on your OnModelCreating method.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDbFunction(
typeof(DBF).GetMethod(nameof(DBF.JsonValue))!
).HasName("JSON_VALUE").IsBuiltIn();
modelBuilder.HasDbFunction(
typeof(DBF).GetMethod(nameof(DBF.JsonQuery))!
).HasName("JSON_QUERY").IsBuiltIn();
/// ...
modelBuilder.Entity(entity => {
//treat entity as text
entity.Property(x => x.Metadata)
.HasColumnType("varchar")
.HasMaxLength(8000);
});
}
Call them dynamically with LINQ.
var a = await _context.FileInformation
.AsNoTracking()
.Where(x => x.Metadata!.JsonValue("$.Property1") == "some value")
.Select(x => x.Metadata!.JsonValue("$.Property2"))
.ToListAsync();
You can add casts or even build anonymous types with this method.
My solution was I added a new class which has KEY and VALUE , which will represent the dictionary i needed :
public class DictionaryObject
{
public string Key { set; get; }
public string Value { set; get; }
}
and instead of having this line in the JSON class :
public Dictionary<string, string> Name { get; set; }
I changed to :
public List<DictionaryObject> Name { get; set; }
Hope it helps.

Setting lifetime manager for registrations done using UnityConfiguration scanner

I have a ASP.NET MVC4 application and am using Unity for IOC. I am using Unity.MVC4 and UnityConfiguration Nuget packages to help with the registration.
I need to automatically register a load of interfaces and their related types to the Unity container. To do this I created a dummy interface; IDependencyInjectionScanner that all my real interfaces inherit from. Below is the code showing that.
public interface IDependencyInjectionScanner
{
}
public interface IChair : IDependencyInjectionScanner
{
NumberOfLegs { get; set; }
}
public class Chair : IChair
{
public NumberOfLegs { get; set; }
}
public interface ITable : IDependencyInjectionScanner
{
NumberOfChairs { get; set; }
}
public class Table : ITable
{
public NumberOfChairs { get; set; }
}
I then used UnityConfiguration to bind the registrations using the scanner. I have get the interfaces being correctly resolved in the controller. Below is the code that shows how I did the binding.
Scan(scan =>
{
scan.AssembliesInDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
scan.With<FirstInterfaceConvention>();
scan.Include(x => (x.GetInterface(typeof(IDependencyInjectionScanner).Name) != null));
scan.ForRegistries();
});
The problem is that I want to register all the types found by the scanner using the hierarchical lifetime manager but can figure out how to do this. The GitHub page for UnityConfiguration https://github.com/thedersen/UnityConfiguration states that this could be achieved by the code below:
Configure<IChair>().AsHierarchicalControlled();
However I if I have to do that for each of the interfaces bound by the scanner then the scanner is of no use as I may as well do:
Register<IChair, Chair>().AsHierarchicalControlled();
Can someone assist me with finding a solution to this please.
Here's an answer to your question using UnityConfiguration. You can create a custom convention to configure the lifetime. Just be careful because it looks like the calls within the Scan() method are order dependent.
public class HierarchicalLifetimeConvention : IAssemblyScannerConvention
{
public void Process(Type type, IUnityRegistry registry)
{
registry.Configure(type).AsHierarchicalControlled();
}
}
and then add that to your Scan() call...
Scan(scan =>
{
scan.AssembliesInDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
scan.With<FirstInterfaceConvention>();
scan.With<HierarchicalLifetimeConvention>(); //<-- New convention
scan.Include(x => (x.GetInterface(typeof(IDependencyInjectionScanner).Name) != null));
scan.ForRegistries();
});
As suggested by #TylerOhlsen I used the built-in Registration by Convention feature of Unity 3.0. I have got it to add the registration mappings and they are using the hierarchical lifetime manager. below is the code for that
container.RegisterTypes(
AllClasses.FromLoadedAssemblies().Where(
t => t.GetInterface(typeof(IDependencyInjectionScanner).Name) != null),
WithMappings.FromMatchingInterface,
WithName.Default,
WithLifetime.Hierarchical);
There is one thing that is disturbing me; when I look at the registrations I have 4 (based on the example code above). 2 type mappings for the Chair type and 2 type mappings for the Table type.
Can anyone shed any light on why this is, as I was only expecting two mappings.

Can I specify a discriminator column with a table-per-type mapping?

I have a class hierarchy that I want to map across several tables using Entity Framework 4.1 Code First. It's like table-per-type (TPT) but I also want a discrimator column.
The hierarchy looks something like:
public class Event
{
public Guid Id { get; set; }
public string Code { get; set; } // discriminator
public DateTime Date { get; set; }
}
public class Party : Event
{
public int AttendeeCount { get; set; }
}
public class BirthdayParty : Party
{
public int Age { get; set; }
}
public class WeddingParty : Party
{
public string Surname { get; set; }
}
This is a pretty weak example but I hope it makes sense. There'll be an "Events" table, a "Parties" table and a table for each kind of party. However, the discriminator column ("Code") will have a known value for each kind of event, like "BIRTH" for birthday parties or "WEDDING" for wedding parties.
The idea is that if I query for just birthday parties on a given date, EF would know to add Code = 'BIRTH' to my query instead of doing a bunch of UNIONs and JOINs to work out which rows it needs.
I map my lowest-level classes like this:
var bd = modelBuilder.Entity<BirthdayParty>();
bd.ToTable("BirthdayParties");
bd.Property(p => p.Age).HasColumnName("BirthdayAge");
I now need to specify the discriminator value in there somehow. I've tried this:
modelBuilder.Entity<Event>().Map<BirthdayParty>(cfg =>
{
cfg.Requires("Code").HasValue("BIRTH");
});
... but that complains that I haven't specified the table name inside the call to Map. So I tried moving the ToTable call into there:
var bd = modelBuilder.Entity<BirthdayParty>();
bd.Property(p => p.Age).HasColumnName("BirthdayAge");
modelBuilder.Entity<Event>().Map<BirthdayParty>(cfg =>
{
cfg.Requires("Code").HasValue("BIRTH");
cfg.ToTable("BirthdayParties");
});
... and now it thinks I want a "Code" column in the "BirthdayParties" table, which is not correct. I've already told it that the "Code" column is in the "Events" table.
Is this even possible? Can I combine the use of a discriminator column with a table-per-type mapping?
Unfortunately this is not supported. Discriminator column can be used only in TPH. TPT differs entity types by mapped tables and it always produces those terrible queries. It could be nice feature so perhaps suggestion on Data UserVoice would make it implemented one day.
Update
There is already a suggestion on user voice for this titled "Discriminator column support in TPT inheritance".
I did an override on SaveChanges to accomplish something similar. I simply added an attribute onto the abstract class called Descriminator and set it based on the Concrete Class Name anytime something new is added.
public class MyContext : DbContext
{
public override int SaveChanges()
{
foreach (var item in ChangeTracker.Entries().Where(x=>x.Entity is MyAbstractClass && x.State == EntityState.Added))
{
((MyAbstractClass)item.Entity).Descriminator = item.Entity.GetType().Name;
}
return base.SaveChanges();
}
}

EF 4.1 Code First doesn't create column for List<string>

I have been playing around quite a lot with EF4 Code First and I do love it. However, I cannot seem to sort this easy one out.
When trying to create something like this, no columns are created in my database:
public IList<String> Recievers { get; set; }
public List<String> RecieversTest { get; set; }
public virtual List<String> RecieversAnotherTest { get; set; }
public virtual ICollection<Int32> RecieversAnotherTest { get; set; }
Ive tried Annotations to map it to a different column name, I've tried IEnumerable and all sorts of other collections, but it refuses to create a column for it.
After an hour on google I found one that claims she has done it, but I'm starting to doubt that. Should it even be possible?
I can't really see why it just doesn't create a column and use JSON or CSV.
It can't be that rare, can it? In my case i just want to store a list of emails.
What am I missing? The project creates all other types without problems, and I've inspected the database to see how other properties I add to test with gets created, while these gets ignored.
So the problem must lie in some setting I'm missing or some configuration....
EF 4.1 RTW on an SQL Server 2008 db.
I have bad news for you. EF doesn't do anything like that. If you want any serialization and deserialization you must do it yourselves = you must expose and map property with serialized value:
private IList<String> _receivers;
// This will be skipped
public IList<String> Receivers
{
get
{
return _receivers;
}
set
{
_receivers = value;
}
}
// This will be mapped
public string ReceiversSer
{
get
{
return String.Join(";", _receivers);
}
set
{
_receivers = value.Split(';').ToList();
}
}
Now ReceiversSer will be mapped to a column in the database.
You can't have a column based on a collection/list of something. A column is a singular item such as public string Receiver.
If you are expecting EF CF to take your IList or List and make several Columns out of it you are correct in that it won't.
In EF CF you create lists in your Entity to represent a relationship to another table. An Order may have many Items in it. You would in this case have an Order class with a list to an OrderItem object.
You would then have an OrderItem class to describe the OrderItem table. This would then essentially represent the 1 to many relationship of Order and OrderItems.

Entity Framework/Linq to sql model to business model

I'm coming from a stored procedure and creating the data access layer manually approach. I am trying to understand where I should fit Linq To SQL or entity frameworks into my normal planning. I normally seperate out the business layer from the DAL layer and use a repository inbetween.
It seems that people will either use the generated classes from linq to sql, extend them by using the partial class or do a full seperation and map the generated linq classes to seperate business entities. I am partial to the seperate Business entities. However, this seems to be counterintuitive.
One of my last projects used DDD and the entity framework. When needing to udpate an object it moved the business entity to the repistory layer which when going to the DAL layer would create a context and than requery the object. It would than update the values and resbumit.
I didn't see the large point as the data context wasn't saved and required an extra query to grab the object before updating. Normally I would just do the update(If concurrency wasn't an issue)
So my questions come down to:
Does it make sense to seperate linq to sql generated classes into Business entities?
Should the data context be saved or is that impractical?
Thanks for your time, trying to make sure I understand. I normally like to seperate out as it makes it cleaner to understand even in some smaller porjects.
I currently hand roll my own Dto classes and Datacontext instead of using auto-generated code files from Linq to Sql. To give some background of my solution architecture/modeling, I have a "Contract" project, and a "Dal" project. (Also a "Model" project, but I'll try to stay focused here on Dal only). Hand-rolling my own Dtos and Datacontext, makes everything a lot smaller and simpler, I'll give a few examples of how I do that here.
I never return out a Dto object outside of the Dal, in fact I make sure to declare them as internal. The way I return them out is I cast them as an interface (interfaces are located in my "Contract" layer). We'll make a simple "PersonRepository" that implements an "IPersonRetriever and IPersonSaver" interfaces.
Contracts:
public interface IPersonRetriever
{
IPerson GetPersonById(Guid personId);
}
public interface IPersonSaver
{
void SavePerson(IPerson person);
}
Dal:
public class PersonRepository : IPersonSaver, IPersonRetriever
{
private string _connectionString;
public PersonRepository(string connectionString)
{
_connectionString = connectionString;
}
IPerson IPersonRetriever.GetPersonById(Guid id)
{
using (var dc = new PersonDataContext(_connectionString))
{
return dc.PersonDtos.FirstOrDefault(p => p.PersonId == id);
}
}
void IPersonSaver.SavePerson(IPerson person)
{
using (var dc = new PersonDataContext(_connectionString))
{
var personDto = new PersonDto
{
Id = person.Id,
FirstName = person.FirstName,
Age = person.Age
};
dc.PersonDtos.InsertOnSubmit(personDto);
dc.SubmitChanges();
}
}
}
PersonDataContext:
internal class PersonDataContext : System.Data.Linq.DataContext
{
static MappingSource _mappingSource = new AttributeMappingSource(); // necessary for pre-compiled linq queries in .Net 4.0+
internal PersonDataContext(string connectionString) : base(connectionString, _mappingSource) { }
internal Table<PersonDto> PersonDtos { get { return GetTable<PersonDto>(); } }
}
[Table(Name = "dbo.Persons")]
internal class PersonDto : IPerson
{
[Column(Name = "PersonIdentityId", IsPrimaryKey = true, IsDbGenerated = false)]
internal Guid Id { get; set; }
[Column]
internal string FirstName { get; set; }
[Column]
internal int Age { get; set; }
#region IPerson implementation
Guid IPerson.Id { get { return this.Id; } }
string IPerson.FirstName { get { return this.FirstName; } }
int IPerson.Age { get { return this.Age; } }
#endregion
}
You will need to add the "Column" attribute to all of your Dto properties, but if you notice, if there is a one-to-one correlation between what you want the field to be exposed as on the interface, and the name of the actual table column, you won't need to add any of the Named Parameters. In this example my PersonId in the database is stored as "PersonIdentityId", yet I only want my interface to make the field say "Id".
That's how I do my Dal layer, I believe this layer should be dumb, real dumb. Dumb in the sense that it is only there for CRUD (Create, Retrieve, Update and Delete) operations. All of the business logic would go into my "Model" project, which would consume and utilize the IPersonSaver and IPersonRetriever interfaces.
Hope this helps!