0

Using database first entity framework. Inserting records into one of my tables, no problem at all. Then I discovered under certain, very rare, circumstances, I was getting duplicate records, which are actually ok. So my plan to deal with those was to create an identical table, catch the exception, put the duplicate in the identical table and examine it at some later stage.

However, I naively thought as they were identical tables, I could just insert the object I have already created into the other "error" table, which obviously I can't do ("cannot convert from 'original_class' to 'new_class'"), which I understand.

So is there a better way to do what I am trying to achieve, or is there a simple way to convert my 'original_class' to my 'new_class' as they are absolutely identical?

***Edit Thought it may be beneficial to add some code for clarity (not my actual code, just a representation of what I am trying to achieve)

My Transaction Class

public partial class Tran
{
    public string tranDescription
}

My Transaction Error Class

public partial class TranError
{
    public string tranDescription
}

How I am trying to process the error

using(var db = new tranEntities())
{
    Tran t = new Tran();
    t.tranDescription = @"Some Description";
    try
    {
        db.Trans.Add(t);
    }
    catch (System.Data.Entity.Infrastructure.DbUpdateException)
    {
        db.TransError.Add(t);
    }
}

I just typed that on the fly, so forgive any typos, I think you get the idea.

SkinnyPete63
  • 597
  • 1
  • 5
  • 20

3 Answers3

2

You could make a method that returns a new object as:

public ErrorObject ConvertToErrorObject(OriginalObject originalObject)
{
    var errorObject = new ErrorObject();
    errorObject.Property1 = originalObject.Property1;
    errorObject.Property2 = originalObject.Property2;

    return errorObject;
}

Then just save the error object as normal.

If this happens on multiple types you could make a generic method that uses reflection and goes through the OriginalObject properties, gets there values, and sets them on the ErrorObject properties with the same name.

Generic method (properties with the same names have to be of the same type):

    public static Tout CloneAndCast<Tin, Tout>(Tin source)
        where Tin : class
        where Tout : class, new()
    {
        if(source != null)
        {
            var clone = new Tout();

            IEnumerable<PropertyInfo> membersIn = typeof(Tin).GetProperties();
            PropertyInfo[] membersOut = typeof(Tout).GetProperties();

            foreach(PropertyInfo member in membersIn)
            {
                membersOut.FirstOrDefault(o => o.Name == member.Name)?.SetValue(clone, member.GetValue(source));
            }

            return clone;
        }
        else
        {
            return null;
        }
    }
  • Hi @Tomislav. OK, that's definitely an option, I just wondered if there was a simpler method (I am not really a programmer, just coding to get by!). My object does only have 6 attributes, so it wouldn't be too onerous to take this route. – SkinnyPete63 Sep 24 '19 at 11:37
  • In my opinion every other option would be more complicated. If this happens multiple times, a different approach might be better, but if it is just once this one is ok. Maybe someone else has a simpler solution. – Tomislav Baljint Sep 24 '19 at 11:44
2

An alternative to Tomislav's answer, if your project is using JSON you can switch identical types using JSON serialization:

public static class SwapperExtensions
{
    public static T2 Swap<T1,T2>(this T1 original) where T1:class where T2:class,new()
    {
        if (original == null)
            return null;

        try
        {
            var result = JsonConvert.SerializeObject(original);
            var newObject = JsonConvert.DeserializeObject<T2>(result);
            return newObject;
        }
        catch(Exception ex)
        {
            throw new ArgumentException("The provided types could not be swapped. See inner exception for details.", ex);
        }
    }
}

which then looks like:

var tran = new Tran { TranDescription = "Some description." };

// do stuff...

var tranError = tran.Swap<Tran, TranError>();

Obviously you would need to guard against exceptions when doing something like this if it's called with incompatible types. :)

Steve Py
  • 26,149
  • 3
  • 25
  • 43
  • thanks for the answer, like your thinking. However, I am a man af little brain, and not a developer, I just code to get by, so I am going for the most simplistic answer! – SkinnyPete63 Sep 25 '19 at 06:55
  • I also thought of inheritance over copying classes (which you could also do using AutoMapper). @SkinnyPete63 inheritance is possible with EF DB First. There are a few different ways of achieving it too but check this example out: https://stackoverflow.com/a/22263190/263832 – xr280xr Sep 26 '19 at 16:30
1

I think a decent way to do it would be to create a base class with all the properties

public class TranBaseClass {
     public string tranDescription
     // other properties
}

Then both Tran and TranError could inherit from those:

public partial class Tran : TranBaseClass {}

public partial class TranError : TranBaseClass {} 

Then I would create constructors for both Tran and TranError which would accept the base class as a parameter:

var tran = new Tran(TranBaseClass)
var tranError = new Tran(TranBaseClass)

And your code could be something like:

using(var db = new tranEntities())
{
    var t = new TranBaseClass();
    t.tranDescription = @"Some Description";
    try
    {
        db.Trans.Add(new Tran(t));
    }
    catch (System.Data.Entity.Infrastructure.DbUpdateException)
    {
        db.TransError.Add(new TranError(t));
    }
}
Stackberg
  • 300
  • 3
  • 12
  • Thanks for this @Stackberg. My issue with this solution is that as I am using EF database first, if I have to update my model from the db, this will get overwritten :( – SkinnyPete63 Sep 24 '19 at 13:23
  • just tried this and where I try to create the constructor for tran, i.e. var tran = new Tran(TranBaseClass) I get "TranBaseClass is a type, which is not valid in the given context" – SkinnyPete63 Sep 24 '19 at 13:34
  • @SkinnyPete63 you will need to pass an instance of TranBaseClass in stead of the class itself. public Tran (TranBaseClass tranBaseClass) { // set properties } and then you can use: var t = new TranBaseClass(); t.tranDescription = @"Some Description"; var tran = new Tran(t); – Stackberg Sep 24 '19 at 14:05