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

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.

Related

One to One HasRequired configuration not working

I have two sets of objects: Coupon and DiscountScheme.
Each have a connected object of {Type}Action, and identical configurations.
When making a request for Coupon, I do not get anything back,
but the same query for DiscountScheme works as expected
A condensed version of the classes (The full code and sql for the tables can be found here):
public class CouponAction
{
public int Id { get; set; }
public virtual Coupon Coupon { get; set; }
}
public class Coupon
{
public int Id { get; set; }
public virtual CouponAction Action { get; set; }
}
public class DiscountSchemeAction
{
public int Id { get; set; }
public virtual DiscountScheme DiscountScheme { get; set; }
}
public class DiscountScheme
{
public int Id { get; set; }
public virtual DiscountSchemeAction Action { get; set; }
}
The configuration:
public class CouponActionMap : EntityTypeConfiguration<CouponAction>
{
public CouponActionMap()
{
ToTable("CouponAction");
}
}
public class CouponMap : EntityTypeConfiguration<Coupon>
{
public CouponMap()
{
ToTable("Coupon");
HasRequired(c => c.Action);
}
}
public class DiscountSchemeActionMap : EntityTypeConfiguration<DiscountSchemeAction>
{
public DiscountSchemeActionMap()
{
ToTable("DiscountSchemeAction");
}
}
public class DiscountSchemeMap : EntityTypeConfiguration<DiscountScheme>
{
public DiscountSchemeMap()
{
ToTable("DiscountScheme");
HasRequired(ds => ds.Action);
}
}
The query I am trying to make:
using(var context = new Context()/* My database context, using a custom wrapper framework*/)
{
Console.WriteLine(context.Coupons.ToList()); // nothing
Console.WriteLine(context.DiscountSchemes.ToList()); // the contents of the table
}
If I query the actions table, I do get the contents, but again for CouponAction I do not get the connected Coupon, and for DiscountScheme it works as expected.
The issue is with your 1-to-1 relationship. By default EF expects a 1-to-1 to be using the PKs on both tables. By putting a CouponID on your CouponAction you are not setting a 1-to-1 relationship, you are setting a 1-to-many/many-to-1. Nothing stops several CouponAction records from having the same CouponId. You could put a unique constraint on CouponID, but if that were the case then you may as well have the CouponAction's PK to be the CouponID. Hence, this is why I don't advise using "Id" as a PK name, but rather CouponId vs. DiscountId, etc.
If the relationship between coupon and action is truly 1-to-1 then get rid of the CouponId on the Action table, and ensure you're using the same ID value across both tables for the related records. You can test this by changing your mapping to configure EF to use CouponId on the CouponAction as it's PK. Once you do that, you should see your related records coming up.
Alternatively you can establish a many to 1 relationship (HasOne.WithMany()) from Action to Coupon, but no return reference without a CouponActionId on Coupon. Or you can set up a 1-to-many where Coupon contains an ICollection<CouponAction> CouponActions even though you intend to only have one action per coupon. But if it is 1-to-1 then I would highly recommend using the same PK value across both tables.

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; }

Create inheritable LINQ-to-SQL mapable class

I am building a library for Windows Phone 8 which requires local databases. For obvious reasons, user of the library is going to create a mappable LINQ-to-SQL class with appropriate [Table]s and [Column]s. However, To in every such class, I need a few more columns for internal functioning of the library. The idea was that I would include a base class in the library which will have members corresponding to the required columns. The user would simply inherit from this class, add his own members and use that class as final LINQ-to-SQL map.
So far, my base class looks like this:
//Class to contain all the essential members
[Table]
public class SyncableEntityBase : NotifyBase, ISyncableBase
{
[Column(DbType = "INT NOT NULL IDENTITY", IsDbGenerated = true, IsPrimaryKey = true)]
public int ItemId { get; set; }
[Column]
public bool IsDeleted { get; set; }
[Column]
public DateTime RemoteLastUpdated { get; set; }
[Column]
public DateTime LocalLastUpdated { get; set; }
}
And the derived class, something like this:
[Table]
public class ToDoCategory : SyncableEntityBase
{
private string _categoryName;
[Column]
public string CategoryName
{
get
{
return _categoryName;
}
set
{
if (_categoryName != value)
{
NotifyPropertyChanging("CategoryName");
_categoryName = value;
NotifyPropertyChanged("CategoryName");
}
}
}
private string _categoryColor;
[Column]
public string CategoryColor
{
get
{
return _categoryColor;
}
set
{
if (_categoryColor != value)
{
NotifyPropertyChanging("CategoryColor");
_categoryColor = value;
NotifyPropertyChanged("CategoryColor");
}
}
}
}
Idea is to have final class with the four essential columns and two added by user. According to MSDN documentation here, I need to append [InheritanceMapping] which requires the inherited type. However, as I am building a library, I have no way to know what types (and how many) the user will derive from my base class. Is there any way around this? How?

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

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.

Connect other models with user in Silverlight Business Application

I'm having a real trouble with what I need to do.
Here's the thing:
I'm creating Silverlight Business Application. I want users to be able to define their own "reminders" and "templates". It seems very simple, just 3 models, 2 one-to-many relations and that's all. But I have no idea how I can connect the existing User model to other models.
I tried to create my own "membership" provider - I've created db with all 3 models and it seemed to be ok, I created EntityModel, but now I have 2 different places where User class is defined, and in the first one it inherits UserBase class and in another EntityObject (in the file Model.Designer.cs, which is generated automatically.
I'm totally confused - can I stick with the EntityObject solution, delete other definitions of classes? If so, how can I still be able to use all the features that come with silverlight business application? (Authentication/Registering etc. is already provided).
We have implemented this scenario in our LOB app.
Firstly add the appropriate properties to the user class like so.
public partial class User : UserBase
{
public Guid UserId { get; set; }
public int PeopleId { get; set; }
public int EpothecaryUserId { get; set; }
public string PersonFullName { get; set; }
public SearchGroups SearchGroups { get; set; }
public string SearchHistoryString { get; set; }
public int SearchRowsReturnedPerGroup { get; set; }
}
Then create a class derived from AuthenticationBase
public class AuthenticationService : AuthenticationBase<User>
{
protected override User GetAuthenticatedUser(IPrincipal principal)
{
return base.GetAuthenticatedUser(principal).WithProfile();
}
[Invoke]
public void SaveMyUser(User user)
{
if (user.UserId == Guid.Empty)
{
ClientLogger.Error("SaveMyUser failed because the UserId is invalid");
return;
}
using (var db = new Pharma360Model())
{
var userProfile = db.UserProfiles.Single(p => p.EpothecaryUserId == user.EpothecaryUserId);
userProfile.SearchGroups = (int)user.SearchGroups;
userProfile.SearchHistory = user.SearchHistoryString;
userProfile.SearchRowsReturnedPerGroup = user.SearchRowsReturnedPerGroup;
db.SaveChanges();
}
}
}
And this will take care of the loading and saving of the custom User class.

Categories