EntityFramework - map a complex property to a json (string) column - c#

I have the following entity:
public class SampleClass
{
public int Id { get; set; }
public object Args {get; set; }
}
Because Args can be of different types and doesnt need to be queryable, I want to store it in the Database as a json string.
I know the following workaround would solve my problem:
public class SampleClass
{
public int Id { get; set; }
public object Args { get { return Json.Deserialize(ArgsJson); } set { ArgsJson = Json.Serialize(value); } }
public string ArgsJson {get; set; }
}
But this is pretty ugly as it exposes information not related to the model and it contains logic again not related to the model.
What I would like to do, is something like that:
public class SampleClassMapper : EntityTypeConfiguration<SampleClass>
{
public SampleClassMapper()
{
this.Property(e => e.Args).MapAs<string>(arg => Json.Serialize(arg), str => Json.Deserialize(str));
}
}
Is there any cool way of doing so?
(I'm using .Net 4.0 with EntityFramework 5 and Sql Server 2008 if it helps)

The way that you do is the only one available for now in EF. Currently EF Code First don't have any easy way to change the object serialization but this can be done modifying the EDMX file at runtime.

Related

Using a stored procedure in an expression

I am using this Expression to create my ViewModel from Database Models:
ViewModel.cs:
public class TestViewModel
{
public static Expression<Func<TestModel, TestViewModel>> Projection => dbModel => new TestViewModel
{
Name = dbModel.Name,
PropertiesCommaSeparated = string.Join(", ", dbModel.TestProperties.Select(x => x.TestProperty.Value))
};
public string Name { get; set; }
public string PropertiesCommaSeparated { get; set; }
}
Since string.Join("","") is not supported in Linq to SQL this fails.
According to this answer https://stackoverflow.com/a/934668 I want to use a stored procedure.
Is it possible to execute a stored procedure in an expression like this?
I am using Entity Framework .NET Core 3.1
This is not something you want to do in a ...ViewModel. ViewModels, DTOs ideally shouldn't have any logic unrelated to views (in your case: DB operations).
With that in mind, I'd fetch values beforehand and map those values inside a static factory method [0]:
public class TestViewModel
{
public static TestViewModel FromModel(TestModel model)
{
return new TestViewModel
{
Name = model.Name,
PropertiesCommaSeparated = string.Join(", ", model.TestProperties.Select(it => it.TestProperty.Value))
};
}
public string Name { get; set; }
public string PropertiesCommaSeparated { get; set; }
}
This means you need to make sure you fetch TestProperties property from the database. Assuming TestProperties is a navigation property for a 1-to-many relationship, you would write:
var record = _dbContext.TestModel.Include(e => e.TestProperties).First();
var vm = TestViewModel.FromModel(record)
return View(vm);
One disadvantage here (still going off on the mentioned assumption) is if TestProperty entity is very large with a lot of columns, you might not want to fetch all columns from the database, but only the Value. But from the looks of it, you probably have something like this, so I wouldn't worry too much about it.
class TestProperty
{
public string Key { get; set; }
public string Value { get; set; }
}
[0]: I could have used a constructor here too, but I think static factory FromModel is more expressive of its intent. Both are equally valid approaches.

Is it possible to have extra (ignored) properties in C#?

I have a repository for a DocumentDb database. My documents all have a set of common properties so all documents implement the IDocumentEntity interface.
public interface IDocumentEntity {
[JsonProperty("id")]
Guid Id { get; set; }
[JsonProperty("documentClassification")]
DocumentClassification DocumentClassification { get; set; }
}
public class KnownDocument : IDocumentEntity {
[JsonProperty("id")]
Guid Id { get; set; }
[JsonProperty("documentClassification")]
DocumentClassification DocumentClassification { get; set; }
[JsonProperty("knownProperty")]
string KnownProperty { get; set; }
}
public class BaseDocumentRepository<T> where T : IDocumentEntity {
public Set(T entity) {
// ... stuff
}
}
This works fine with a KnownDocument where I know all of the properties. But, of course, what's great about a Document Db is that I don't need to know all of the properties (and in many cases I won't).
So my client submits something like this-
{unknownProperty1: 1, unknownProperty2: 2}
And I want to upsert this using my document repository.
public OtherDocumentService() {
_otherDocumentService = new OtherDocumentRepository();
}
public UpsertDocument(dynamic entity) {
entity.id = new Guid();
entity.documentClassification = DocumentClassification.Other;
_otherDocumentRepository.Set(entity);
}
But I get an InvalidCastException from dynamic to IDocumentEntity. I assume it's because of the extra properties that exist on the dynamic object but not on the IDocumentEntity interface?
What I'm trying to do is leave my document entities open to be dynamic, but rely on a few properties being there to maintain them.
Entity parameter passed to the UpsertDocument should explicitly implement IDocumentEntity in order do make the code works, it is not enough just have a Id property.
Some options:
1) Proxy may be applied:
public class ProxyDocumentEntity : IDocumentEntity
{
public dynamic Content { get; private set; }
public ProxyDocumentEntity(dynamic #content)
{
Content = #content;
}
public Guid Id
{
get { return Content.Id; }
set { Content.Id = value; }
}
}
... using
public void UpsertDocument(dynamic entity)
{
entity.Id = new Guid();
repo.Set(new ProxyDocumentEntity(entity));
}
The stored document will have nested Object property, which may be not acceptable
2)There is a lib https://github.com/ekonbenefits/impromptu-interface which creates a proxy dynamically
and does not make extra property like solution above.
Drawback will be in performance.
Technically it could be 2 methods:
public void UpsertDocument(IDocumentEntity entity){...}
public void UpsertDocument(dynamic entity){...}
so the first (fast) will work for the objects which implement IDocumentEntity and second(slow) for the rest of the objects.
But this is a speculation a bit , as I dunno the details of the whole code base of the project you have.
If you have some flexibility as to how to name those dynamic properties, you could stuff them into a Dictionary property on your object:
public Dictionary<string, dynamic> extra { get; set; }

Automapper - How to map a private backing field

i'm trying to use Automapper in this scenario.
I've got an Entity (DDD entity object) that must have private setters for all the properties and collections and i have to map it to a simpler object that will be stored in DB using.
The Entity have a code like that:
public class TypeA : Entity
{
private List<TypeB> _assignedItems;
public IEnumerable<TypeB> AssignedItems
{
get { return _assignedItems.ToList(); }
}
public string Name { get; private set; }
public string Description { get; private set; }
...etc...
}`
And the Persistence-friendly object
[Table("TypeA")]
public class TypeADao : EntityDao
{
public string Name { get; set; }
public string Description { get; set; }
public ICollection<TypeBDao> AssignedItems { get; set; }
}
With Automapper can easily Map the Entity to the Dao, but i fail to do the opposite, as i need to map AssignedItems to the private backing field _assignedItems in the Entity.
How can i do that?
Is there a way to map the AssignedItems collection to the private field called _assignedItems?
Many thanks to all
I know this might come a bit too late, but should still be helpful for people who might encounter this problem in the future.
Here is how I solved the mapping private field problem.
// Please refer to https://github.com/AutoMapper/AutoMapper/issues/600
// Please refer to https://github.com/AutoMapper/AutoMapper/issues/946
ShouldMapField = fieldInfo => !fieldInfo.IsDefined(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute));
ShouldMapProperty = propertyInfo => true;

Config Automapper to ignore type when it's an inner-inner property but not inner property

This one takes a little explaining. I have a set of types such that;
public class Child
{
public int ID { get; set;}
}
public class MayHaveChild
{
public Child Value { get; set; }
public int MayID { get; set; }
}
public class MustNotHaveChild { get; set; }
{
public List<MayHaveChild> MayValues { get; set; }
}
In the above scenario, I want any mapping of MayHaveChild to have the values for the Child object, except when I have mapped MustNotHaveChild. E.g.;
When I have
//...some code
MayHave obj = Mapper.Map<MayHaveChild>(childObj);
// I want to be able to access obj.Child.ID
But when I have
//...some code
MustNotHave obj = Mapper.Map<MustNotHaveChild>(notHaveObj);
// I want to be able to access obj.MayValues[0].MayID but
// *not* obj.MayValues[0].Value
I've been through the automapper documention on nesting, polymorphism, lists, etc and I can't find anything that quite matches what I want.
I could solve this by having a inheriting the MayHave class to a MustNotHave variant but this would involve changing quite a lot of existing code. Is there a way to configure Automapper in the manner I need?
I couldn't find a way to configure AutoMapper the way I wanted without going down the inheritance route - though this proved less problematic than I thought. I did something like the following;
public class NoChild : MayHaveChild
{
}
public class MustNotHaveChild { get; set; }
{
// \/--datatype change here
public List<NoChild> MayValues { get; set; }
}
Then, later in the AutoMapper config;
Mapper.CreateMap<MayHave, NoChild>()
.ForMember(c => c.Child, opt => opt.Ignore());

How can I loosely couple database columns in a .NET application?

I have two versions of an almost identical database. Below I have created an Example table to demonstrate the basic differences, namely the ID column has changed from an Integer Identity to a GUID and various properties have been updated, in the Example archived has been replaced with readOnly and hidden:
Legacy version:
CREATE TABLE Example
(
--Data Identity (maps to DbId in the example code)
Id int IDENTITY PRIMARY KEY,
--Example columns
SomeValue varchar(50),
AnotherValue int,
--Data Properties
Archived bit
)
New version:
CREATE TABLE Example
(
--Data Identity (maps to DbId in the example code)
Id uniqueidentifier PRIMARY KEY,
--Example columns
SomeValue varchar(50),
AnotherValue int,
--Data Properties
ReadOnly bit,
Hidden bit
)
I need to be able to use an O/R mapper such as NHibernate to connect to one or other of these database versions. I would like to be able to tell the application which version to use through settings in a configuration file.
My initial plan was to create a common interface for the business logic and use an IoC container such as Unity to swap between the relevant concrete classes in the configuration file.
Below is an example of the code I created to test this theory:
public interface IDataIdentity
{
object Id { get; }
}
public class LegacyDataIdentity : IDataIdentity
{
public virtual long DbId { get; set; }
public object Id
{
get { return DbId; }
}
}
public class NewDataIdentity : IDataIdentity
{
public virtual Guid DbId { get; set; }
public object Id
{
get { return DbId; }
}
}
public interface IDataProperties
{
bool ReadOnly { get; set; }
bool Hidden { get; set; }
}
public class LegacyDataProperties : IDataProperties
{
public virtual bool Archived { get; set; }
public bool ReadOnly
{
get { return Archived; }
set { Archived = value; }
}
public bool Hidden
{
get { return Archived; }
set { Archived = value; }
}
}
public class NewDataProperties : IDataProperties
{
public virtual bool ReadOnly { get; set; }
public virtual bool Hidden { get; set; }
}
public class DataItem
{
public DataItem(IDataIdentity dataIdentity, IDataProperties dataProperties)
{
DataIdentity = dataIdentity;
DataProperties = dataProperties;
}
public IDataIdentity DataIdentity { get; set; }
public IDataProperties DataProperties { get; set; }
}
public class Example : DataItem
{
public Example(IDataIdentity dataIdentity, IDataProperties dataProperties)
: base(dataIdentity, dataProperties)
{
}
public virtual string SomeValue { get; set; }
public virtual int AnotherValue { get; set; }
}
Can anyone advise if this is possible (specifically with Unity and NHibernate) and if so how to create the relevant NHibernate mapping files?
Alternatively, can anyone suggest any solution to the problem using any other methods or other IoC and O/R mapping tools (commercial or open source)?
Many thanks,
Paul
Why not abstract your data provider, implementing 2 versions (one with nhibernate mappings for the legacy data item, and one for the new data item).
To paraphrase your code slightly (simplified for clarity):
public abstract class AbstractData
{
public abstract string SomeValue { get; set; }
public abstract bool ReadOnly { get; set; }
//etc.
}
public interface IDataProvider
{
AbstractData Get(object id);
}
public class LegacyData : AbstractData
{
// Implement AbstractData, and
public virtual long Id { get { return m_Id; } set { m_Id = value; };
private long m_Id;
}
public class LegacyDataNHibernateProvider : IDataProvider
{
public LegacyDataProvider()
{
// Set up fluent nhibernate mapping
}
public AbstractData Get(object id)
{
// Interpret id as legacy identifier, retrieve LegacyData item, and return
}
};
// Same again for new data provider
This way, you aren't tied to nhibernate (or a database, for that matter), and you can specify concrete classes with correctly typed identifiers (which nhibernate can handle). This is the approach I'm taking, where I currently have to map against an existing database's SPs, but will later migrate to a new ORM-based system.
Maybe I'm not understanding your question correctly, but it sounds to me like you need to implement something like the "Factory Pattern".
I recently used the factory pattern to code (c#) to two data layers when the company I work for was switching from JDE to SAP. I was able to flip a config switch to switch between the two data layers, and the GUI wouldn't know any different.
Here's a couple links I found:
http://msdn.microsoft.com/en-us/library/ms954600.aspx
http://www.allapplabs.com/java_design_patterns/factory_pattern.htm
http://en.wikipedia.org/wiki/Abstract_factory_pattern
As far as NHibernate goes, I'm not familiar with it... sorry. Hope this helps.
Hard to recommend without knowing the full picture, but.... You can create an abstruction in the SPs by having SP return identical dataset for both table structures.
Another abstruction I am thinking of, is you can actually specify different hybernate mapping files, and initialize Hibernate with a different file depending on what database you connect to.

Categories