Make generic object instantiation more generic - c#

I've got this piece of code to create new objects in a generic way:
var user = User.Create<User>(c => c.Name = "321X");
What I don't like about it is the fact I need to pass the 'generic notation' <T> for every create call. After all I create an object that I'm already referring to...
The code behind this current functionality is:
public class User : CreateBase
{
public string Name { get; set; }
}
public abstract class CreateBase
{
public DateTime CreateDate { get; set; }
public Guid Guid { get; set; }
public static T Create<T>(Action<T> init) where T : CreateBase, new()
{
T obj = new T();
obj.Guid = Guid.NewGuid();
obj.DateTime = DateTime.Now;
init(obj);
return obj;
}
}
Is it possible (and how) to refactor my code to this, to create an object?
var user = User.Create(c => c.Name = "321X");
Thanks!

Define the generic argument on the class level:
public abstract class CreateBase<T> where T : CreateBase<T> , new()
{
public static T Create(Action<T> init)
{
//...
}
}
public class User : CreateBase<User>
{
public string Name { get; set; }
}
Then you can write var user = User.Create(c => c.Name = "321X");
Otherwise the compiler cannot infer the type for your Create method without specifying the type argument.

You were not very far. Try this modification:
public abstract class CreateBase<T> where T : CreateBase<T> , new()
{
public DateTime CreateDate { get; set; }
public Guid Guid { get; set; }
public static T Create(Action<T> init)
{
T obj = new T();
obj.Guid = Guid.NewGuid();
obj.CreateDate = DateTime.Now;
init(obj);
return obj;
}
}
public class User : CreateBase<User>
{
public string Name { get; set; }
}
EDIT: Updated the code after I tested it on my local environment. It works now.

You are doing it the wrong way. Instead of getting rid of the generic argument, get rid of (needlessly) saying User.. Instead:
CreateBase.Create<User>(...)
No more redundancies.
Besides that, calling a static member of the base class through a derived class is an anti-pattern.

A better approach would be to include this functionality in the constructor of the base class (I call it ModelBase)
public abstract class ModelBase
{
public DateTime CreateDate { get; private set; }
public Guid Guid { get; private set; }
public ModelBase()
{
Guid = Guid.NewGuid();
DateTime = DateTime.Now;
}
}
public User : ModelBase
{
public User()
: base()
{
}
public User(string name)
: base()
{
Name = name
}
public string Name { get; set; }
}
Creating a user the standard way will initialize the Guid and date automatically
var user = new User { Name = "xy };
EDIT
I added a second constructor with a name parameter. I you want to force the initialization of the name, drop the first parameterless constructor.
var user = new User("xy");

If you really uncomfortable with that sintax (I, honestly, don't see much problem here)
you can do the following:
public class User : CreateBase
{
public string Name { get; set; }
public static User Create(Action<User> a)
{
return Create<User>(a); //CALL BASE CLASS GENERIC FUNCTION
}
}
After you can call it in a way you would like to do that :
var user = User.Create(c => c.Name = "321X");

Related

How to define a type whose gender can be changed on the fly in code without changing the type of all the properties that are of its type?

I have many entities that use a UserId property of the same type.
Can I define a type (string or int, ...) that I can easily change as a variant for all?
Example:
public class Entity_One
{
public DefineMyType UserId { get; set; }
public string Property_Entity_One { get; set; }
}
public class Entity_Two
{
public DefineMyType UserId { get; set; }
public string Property_Entity_Two { get; set; }
}
const DefineMyType = string;
// or const DefineMyType = int;
// or const DefineMyType = Guid;
Constants can't be used like that.
Preprocessor may be used.
But we can use a generic:
public abstract class AbstractID<T>
{
static protected T Last = default;
public T Value { get; protected set; } // or perhaps init only with C# 9
}
Thus we can define some specialized IDs like:
public class NumberID<T> : AbstractID<T> where T : struct, IComparable, IFormattable
{
public NumberID()
{
Value = (T)( (dynamic)Last + 1 );
Last = Value;
}
}
public class GuidID : AbstractID<Guid>
{
public GuidID()
{
Value = Guid.NewGuid();
Last = Value;
}
}
public class StringID : AbstractID<string>
{
private string Generate()
{
return ...
}
public StringID()
{
Value = Generate();
Last = Value;
}
}
Then we can set the "default" ID type:
public class ManagedID : NumberID<int>
{
}
Or:
public class ManagedID : GuidID
{
}
Therefore we can easily change ManagedID for all code using it.
It only requires to change the ancestor class in the declaration.
And now that works:
public class EntityOne
{
public ManagedID UserId { get; } = new ManagedID();
public string PropertyEntityOne { get; set; }
}
public class EntityTwo
{
public ManagedID UserId { get; } = new ManagedID();
public ManagedID EntityOneId { get; }
public string PropertyEntityTwo { get; set; }
public EntityTwo(EntityOne one)
{
EntityOneId = one.UserId;
}
}
Test
var entity1 = new EntityOne();
var entity2 = new EntityOne();
var entity3 = new EntityTwo(entity1);
Console.WriteLine(entity1.UserId.Value);
Console.WriteLine(entity2.UserId.Value);
Console.WriteLine(entity3.UserId.Value + $" ({entity3.EntityOneId.Value})");
Result with an integer
1
2
3 (1)
Result with a GUID
3a189122-60fd-4dc5-9d7f-3cc4c83375f9
37a9c7de-8ed5-4d02-a1b9-f414db051335
2de962d6-cc91-4e78-b3dc-28acb0ba7f3b (3a189122-60fd-4dc5-9d7f-3cc4c83375f9)
Warning
Here, the use of numbers is very basic and not really reliable, especially beyond a local machine and after stopping the execution of the current process. Thus persistence somewhere of the last value is required for a real database, like in a config file or whatever.
GUID vs INT IDENTITY
Guid vs INT - Which is better as a primary key?
Int for identity/primary key and Guid for public identifier, best practices?

trying to pass generic id's to class constructor

I have below class LibrarySourceTableInput and having structure like as this
public class LibrarySourceTableInput<T> where T: ISourceOfData
{
public LibrarySourceTableInput(List<T> libraries, string mappedLibrarySource)
{
this.LibrarySourceRowInputs = libraries?.Select(l => new LibrarySourceRowInput()
{
LibrarySourceId = l.Id,
SourceOfDataId = l.SourceOfData.Id
}).ToList() ?? new(),
this.MappedLibrarySource = mappedLibrarySource;
}
public List<LibrarySourceRowInput> LibrarySourceRowInputs { get; set; }
public string MappedLibrarySource { get; set; }
}
and then the interface ISourceOfData structure looks like as this below
public interface ISourceOfData : IIdentity
{
public new Guid Id { get; set; }
public CodeStandardGuideline SourceOfData { get; set; }
}
And i am calling above class constructor in other place like as this
var mechanicalData = spaceTypeObject.TargetObject.MechanicalData;
var librarySourceTableInputs = new List<LibrarySourceTableInput<ISourceOfData>>
{
new LibrarySourceTableInput<ISourceOfData>(mechanicalData?.Environments, mappedLibrarySource), // I am getting conversion error here
new LibrarySourceTableInput<ISourceOfData>(mechanicalData?.AirflowsA621 , mappedLibrarySource)
.......
.......
}
and the structure for mechanicaldata.environments is looks like this
public class MechanicalData
{
public List<LibraryEnvironment> Environments { get; set; }
public List<LibraryA621> AirflowsA621 { get; set; }
.......
}
and then last one libraryEnvironment class is looks like this
public class LibraryEnvironment : ISourceOfData
{
public virtual CodeStandardGuideline SourceOfData { get; set; }
.....
......
public Guid Id {get; set;}
}
I have got the below error at this line new LibrarySourceTableInput<ISourceOfData>(mechanicalData?.Environments, mappedLibrarySource)
and the error is Cannot convert from Generic.List<LibraryEnvironment> to Generic.List<IsourceOfData>
Could any one please let me know or any idea on how to send that generic list to that class constructor, Many thanks in advance.
Instead of specifying the interface ISourceOfData (your class already knows the type will have that interface), specify the actual class:
var librarySourceTableInputs = new List<LibrarySourceTableInput<LibraryEnvironment>>
{
new LibrarySourceTableInput<LibraryEnvironment>(mechanicalData?.Environments, mappedLibrarySource),
.......
.......
}

Get existing instance of List<T>

I've inherited a bloated project that uses a huge class as an in-memory database:
public class Database
{
public class Parameter1
{
public string Code { get; set; }
public string Label { get; set; }
public List<Parameter1Value> paramValues;
}
public class Parameter2
{
public string Code { get; set; }
public string Label { get; set; }
public List<Parameter2Value> paramValues;
}
public class Parameter1Value
{
public string Value { get; set;}
public Parameter parameter { get; set;}
}
public class Parameter2Value
{
public int Value { get; set;}
public Parameter2 parameter { get; set;}
}
public List<Parameter1> parameter1List { get; set; }
public List<Parameter2> parameter2List { get; set; }
}
I am creating a generic method that creates instances of Parameter1 or Parameter2 (see below) and should add those to their respective lists, but I don't know how to use those types to get the parameter1List or parameter2List instances from my Database class. The Database class holds only one List<T> property for each defined type. Is this possible?
This is the generic method used to create instances:
public static Database Add<T>(this Database database, string code, string label) where T : new()
{
T itemToCreate = (T)Activator.CreateInstance(typeof(T));
itemToCreate.Code = code;
itemToCreate.Label = label;
var listForItem = database.GetList<T>; // This is the missing functionality
listForItem.Add(itemToCreate);
return database;
}
Here is a solution using interfaces and generic constraints.
Create an interface to represent a generic parameter class and add members to the interface as required:
public interface IParameter { ... }
And an interface to represent a list of parameters:
public interface IParameterList<TParameter> where TParameter : IParameter
{
List<TParameter> ParameterList { get; set; }
}
Have the Database and Parameter classes implement these new interfaces:
public class Parameter1 : IParameter
public class Parameter2 : IParameter
public class Database : IParameterList<Parameter1>, IParameterList<Parameter2>
{
List<Parameter1> IParameterList<Parameter1>.ParameterList { get => parameter1List; set => parameter1List = value; }
List<Parameter2> IParameterList<Parameter2>.ParameterList { get => parameter2List; set => parameter2List = value; }
...
}
Add a where TParameter : IParameter constraint to your generic Parameter factory function, and have the factory function require an argument of type IParameterList<TParameter> which is an instance of the Database class. This satisfies the compiler that the Database class owns a list of TParameter. Now we just do db.ParameterList.Add(r) to add our new parameter to the correct list.
public static TParameter CreateParameter<TParameter>(IParameterList<TParameter> db) where TParameter : IParameter, new()
{
var r = new TParameter(); // This is the generic function you mentioned. Do stuff here to create your Parameter class.
db.ParameterList.Add(r); // Add the newly created parameter to the correct list
return r;
}
Code dump (full working version after I picked up your edit which added the generic factory function):
public class Parameter1 : IParameter
{
public string Code { get; set; }
public string Label { get; set; }
public List<Parameter1Value> paramValues;
}
public class Parameter2 : IParameter
{
public string Code { get; set; }
public string Label { get; set; }
public List<Parameter2Value> paramValues;
}
public class Parameter1Value
{
public string Value { get; set; }
public Parameter parameter { get; set; }
}
public class Parameter2Value
{
public int Value { get; set; }
public Parameter2 parameter { get; set; }
}
public class Database : IParameterList<Parameter1>, IParameterList<Parameter2>
{
// Note: Setters for the List properties probably not needed here or in IParameterList as with the following code we instantiate them at class construction time and, in this MCVE at least, there are no further assignments
public List<Parameter1> parameter1List { get; set; } = new List<Parameter1>();
public List<Parameter2> parameter2List { get; set; } = new List<Parameter2>();
List<Parameter1> IParameterList<Parameter1>.ParameterList { get => parameter1List; set => parameter1List = value; }
List<Parameter2> IParameterList<Parameter2>.ParameterList { get => parameter2List; set => parameter2List = value; }
public static TParameter Add<TParameter>(IParameterList<TParameter> db, string code, string label) where TParameter : IParameter, new()
{
var itemToCreate = new TParameter();
itemToCreate.Code = code;
itemToCreate.Label = label;
db.ParameterList.Add(itemToCreate); // Add the newly created parameter to the correct list
return itemToCreate;
}
}
public interface IParameter
{
string Code { get; set; }
string Label { get; set; }
}
public interface IParameterList<TParameter> where TParameter : IParameter
{
List<TParameter> ParameterList { get; set; }
}
// Testing:
void Main()
{
var db = new Database();
Database.Add<Parameter1>(db, "hello", "hello2");
Database.Add<Parameter1>(db, "hello", "hello2");
Database.Add<Parameter2>(db, "hello", "hello2");
Console.WriteLine($"P1 count (should be 2): {db.parameter1List.Count()}; P2 count (should be 1): {db.parameter2List.Count}");
}
Output:
P1 count (should be 2): 2; P2 count (should be 1): 1
Here is a solution which acquires the target list using generics and reflection:
public static List<T> GetList<T>(this Database dataBase) where T : new()
{
return dataBase.GetType()
.GetProperties()
.Where(x => x.PropertyType == typeof(List<T>))
.Select(x => (List<T>)x.GetValue(dataBase))
.FirstOrDefault();
}
Credit: Michael Randall in the comments

Need solution regarding generic c# method

I am trying to make my method generic and I am stuck at a point and need your assistance. The code scenario is I have an abstract class say MyBaseAbs which contains common properties:
public abstract class MyBaseAbs
{
public string CommonProp1 { get; set; }
public string CommonProp2 { get; set; }
public string CommonProp3 { get; set; }
}
Now I have child classes:
public class Mychild1: MyBaseAbs
{
public string Mychild1Prop1 { get; set; }
public string Mychild1Prop2 { get; set; }
public string Mychild1Prop3 { get; set; }
}
and another child class:
public class Mychild2: MyBaseAbs
{
public string Mychild1Prop1 { get; set; }
public string Mychild2Prop2 { get; set; }
}
Now I have to create a common method which needs to perform some operations on the basis of Mychild1 and Mychild2, so what I did is:
public MyCustomClass SaveOperation<T>(T myObj)
where T : MyBaseAbs
{
SaveObject obj = new SaveObject();
}
so inside this method I need to write common code which does the mapping for SaveObject object according to the child object passed. How can I determine which object is passed and use properties accordingly.
One option would be to create a base Save function in your base class and make it virtual.
Then override the method in your child classes. This way when you call the Save method in your SaveOperation it should call the appropriate method from the correct child class.
public abstract class MyBaseAbs
{
public string CommonProp1 { get; set; }
public string CommonProp2 { get; set; }
public string CommonProp3 { get; set; }
public virtual void Save() { }
}
public class Mychild1: MyBaseAbs
{
public string Mychild1Prop1 { get; set; }
public string Mychild1Prop2 { get; set; }
public string Mychild1Prop3 { get; set; }
public override void Save() {
//Implementation for Mychild1
}
}
public class Mychild2: MyBaseAbs
{
public string Mychild1Prop1 { get; set; }
public string Mychild2Prop2 { get; set; }
public override void Save() {
//Implementation for Mychild2
}
}
If you can't modify your business objects, you can check the type of the concrete class in the SaveOperation method:
public MyCustomClass SaveOperation<T>(T myObj)
where T : MyBaseAbs
{
SaveObject obj = new SaveObject();
if (myObj is Mychild1) {
Mychild1 mychild1 = (Mychild1) myObj;
// Business logic for object of type Mychild1
} else if (myObje is Mychild2) {
Mychild2 mychild2 = (Mychild2) myObj;
// Business logic for object of type Mychild2
}
}
Notice that this is not a very solid solution as, if you are creating new objects that implement your abstract class, you will have to remeber to add another branch in the if statement.
As #BojanB mentioned, the obvious solution would be to create a virtual method in your base class and override it in the derived, but if you cannot modify the code there then you can create a method for each derived class and create a dictionary that maps each type to its method:
private Dictionary<Type, Action<MyBaseAbs, MyCustomClass>> _saveOperations =
new Dictionary<Type, Action<MyBaseAbs, MyCustomClass>>();
//You can then set an entry for each of your derived classes
_saveOperations[typeof(Mychild1)] = (myObj, myCustomObj) =>
{
//Mychild1-specific logic
};
public MyCustomClass SaveOperation(MyBaseAbs obj)
{
//do the common saving operations here
var result = new MyCustomClass();
//....
var actualType = obj.GetType();
if(_saveOperations.ContainsKey(actualType))
{
_saveOperations[actualType](obj, result);
}
return result;
}
You can then add an item to the dictionary for each derived class. It is the same concept as using the is operator but allows you to add methods for more derived types without modifying the original SaveOperation method
You can use C#'s As-Operator as follows:
Mychild1 child1 = myObj as Mychild1;
if(child1 != null) {
//Here you can use child1.Mychild1Prop1 forexample
}
Link to msdn: https://msdn.microsoft.com/en-us/library/cscsdfbt.aspx

T must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'TModel' in the generic type or method

I've tried searching SO for the answer and stumbled upon similar problems but I couldn't manage to use them for solving my problem, so please try not to mark this as a duplicate. Let's move on to the real deal:
I have a generic library for standardizing entity framework database first models.
These are the generic classes that I've created:
public abstract class GenericLookupModel : IActive, ICreated, IModified, IIdentity, IStringValue
{
public bool is_active { get; set; }
public string value { get; set; }
public string description { get; set; }
public DateTime created_on { get; set; }
public string created_by { get; set; }
public DateTime modified_on { get; set; }
public string modified_by { get; set; }
public int id {get;set;}
public void SetCreated(string creator = "SYSTEM")
{
created_by = creator;
created_on = DateTime.Now;
}
public void SetModified(string modifier = "SYSTEM")
{
modified_by = modifier;
modified_on = DateTime.Now;
}
}
And an class for the ViewModel with pre-set MVC attributes
public abstract class GenericLookupViewModel
{
[Key]
public int ID { get; set; }
[Required]
[StringLength(300)]
public string Name { get; set; }
[StringLength(4000)]
public string Description { get; set; }
[Required]
public bool Active { get; set; }
[StringLength(50)]
[DisplayName("Record last modified by")]
public string ModifiedBy { get; set; }
[DisplayName("Record last modified Date")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime ModifiedOn { get; set; }
[StringLength(50)]
[DisplayName("Record created by")]
public string CreatedBy { get; set; }
[DisplayName("Record creation Date")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime CreatedOn { get; set; }
}
Also, I've created a service class that I intend to use inside the controller for getting the data:
public abstract class GenericLookupModelDataService<TModel, TViewModel> : object
where TModel : GenericLookupModel, new()
where TViewModel : GenericLookupViewModel, new()
{
private readonly DbContext _db;
private DbContext entities
{
get { return _db; }
}
public GenericLookupModelDataService()
{
_db =
new DbContext(
System.Configuration.ConfigurationManager.ConnectionStrings["DefaultConnectionString"].ConnectionString);
}
public virtual IEnumerable<TViewModel> ReadAllActive()
{
return entities.Set<TModel>().Where(x => x.is_active).Select(product => new TViewModel
{
ID = product.id,
Active = product.is_active,
Description = product.description,
Name = product.value,
CreatedBy = product.created_by,
CreatedOn = product.created_on,
ModifiedBy = product.modified_by,
ModifiedOn = product.modified_on
});
}
public virtual IEnumerable<TViewModel> Read()
{
return entities.Set<TModel>().Select(product => new TViewModel
{
ID = product.id,
Active = product.is_active,
Description = product.description,
Name = product.value,
CreatedBy = product.created_by,
CreatedOn = product.created_on,
ModifiedBy = product.modified_by,
ModifiedOn = product.modified_on
});
}
public virtual void Create(TViewModel product, string username = "SYSTEM")
{
var entity = new TModel
{
is_active = product.Active,
description = product.Description,
value = product.Name,
};
entity.SetCreated();
entity.SetModified();
_db.Set<TModel>().Add(entity);
_db.SaveChanges();
}
public virtual void Update(TViewModel product, string username = "SYSTEM")
{
var entity = new TModel
{
id = product.ID,
is_active = product.Active,
description = product.Description,
value = product.Name
};
entity.SetModified();
_db.Set<TModel>().Attach(entity);
entities.Entry(entity).State = EntityState.Modified;
entities.SaveChanges();
}
public virtual void Destroy(TViewModel product)
{
var entity = new TModel {id = product.ID};
entities.Set<TModel>().Attach(entity);
entities.Set<TModel>().Remove(entity);
entities.SaveChanges();
}
public virtual TViewModel GetByID(int ID)
{
var item = entities.Set<TModel>().Find(ID);
var result = new TViewModel
{
ID = item.id,
Active = item.is_active,
CreatedBy = item.created_by,
CreatedOn = item.created_on,
Description = item.description,
ModifiedBy = item.modified_by,
ModifiedOn = item.modified_on,
Name = item.value
};
return result;
}
public void Dispose()
{
entities.Dispose();
}
}
The library compiles fine, I use it inside the data layer project inside my MVC App. Start off by creating a new view model:
public class RoleViewModel : GenericLookupViewModel
{
}
Then, lets create a service:
public class RoleService : GenericLookupModelDataService<tblkp_Role, RoleViewModel>
{
}
Make the Entity Framework class inherit from the abstract model:
partial class tblkp_Role : GenericLookupModel
{
}
Finally let's create our controller:
public class EmployeeController : Controller
{
private RoleService roleService;
public EmployeeController()
{
dataService = new EmployeeService();
PopulateLookups();
}
private void PopulateLookups()
{
roleService = new RoleService();
ViewData["roles"] = roleService.ReadAllActive();
}
public ActionResult Index()
{
return View();
}
}
Sorry for the wall-of-code, some code has already been removed for brevity.
While compiling it gives me 3 errors:
UPDATE: Provided tblk_Role class generated automatically by EF (DB First approach):
using System;
using System.Collections.Generic;
public partial class tblkp_Role
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public tblkp_Role()
{
this.tbl_Employee = new HashSet<tbl_Employee>();
}
public int id { get; set; }
public string value { get; set; }
public string desciption { get; set; }
public bool is_active { get; set; }
public System.DateTime created_on { get; set; }
public string created_by { get; set; }
public System.DateTime modified_on { get; set; }
public string modified_by { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<tbl_Employee> tbl_Employee { get; set; }
}
UPDATE 2: Erros in plain text format:
Error 33 'DataLayer.Model.tblkp_Role' must be a non-abstract type
with a public parameterless constructor in order to use it as
parameter 'TModel' in the generic type or method
'MyLib.Model.GenericLookupModelDataService<TModel,TViewModel>' c:\Projects\Sources\MyLib\bin\Release\MyLib.dll
Error 32 The type 'DataLayer.Model.tblkp_Role' cannot be used as type
parameter 'TModel' in the generic type or method
'MyLib.Model.GenericLookupModelDataService<TModel,TViewModel>'. There
is no boxing conversion from 'DataLayer.Model.tblkp_Role' to
'MyLib.Model.GenericLookupModel'. c:\Projects\Sources\MyLib\bin\Release\MyLib.dll
You have the following:
public abstract class GenericLookupModelDataService<TModel, TViewModel> : object
where TModel : GenericLookupModel, new()
where TViewModel : GenericLookupViewModel, new()
{
// ...
That class has two generic paramters called TModel and TViewModel. Each of these has constraints to it, indicated after the where contextual keyword.
For TModel the constraints are:
a base class constraint requiring that the class GenericLookupModel must be a base class of what ever type is substituted in for TModel, and
a constructor constraint new() requiring that the type used for TModel must expose a public instance constructor that takes zero arguments.
One of the errors you ask about is:
Error 33 'DataLayer.Model.tblkp_Role' must be a non-abstract type
with a public parameterless constructor in order to use it as
parameter 'TModel' in the generic type or method
'MyLib.Model.GenericLookupModelDataService<TModel,TViewModel>'
This simply means that the type tblkp_Role which you try to use for TModel does not conform to the constructor constraint. Do you have the 0-parameter constructor?
Another error you ask about is:
Error 32 The type 'DataLayer.Model.tblkp_Role' cannot be used as
type parameter 'TModel' in the generic type or method
'MyLib.Model.GenericLookupModelDataService<TModel,TViewModel>'. There
is no boxing conversion from 'DataLayer.Model.tblkp_Role' to
'MyLib.Model.GenericLookupModel'.
This indicates that the base class constraint is not met. Since the error text talks about "boxing conversion", it appears that the type tblkp_Role which the compiler is using, is in fact a value type (struct type, or enum type). Types like that can never derive from GenericLookupModel as the constraint requires.
It must be that the type tblkp_Role that the C# compiler uses, is another type than the type you define with partial class tblkp_Role : GenericLookupModel. You may have some conflicting names or some duplicate code/names from referenced projects.
In the image version of your compile-time errors we see that the compiler also complains that the type tblkp_Role which you use is declared in an assembly which you have no reference to. Try fixing that one first. Maybe the other ones will go away once the compiler can see all details of tblkp_Role because it has the reference to the project that defines that type.
The error you mentioned is usually faced when you try to use the same generic type parameter in different classes without defining all the constraints in at least one of them. See this Jon Skeet's answer for clarity.
But you are using TModel in only one class here i.e. GenericLookupModelDataService, therefore I tried the following:
I wrote all your code in the same code file that means no external library. Something like this:
class Program
{
static void Main(string[] args)
{
RoleService roleService = new RoleService();
}
}
class RoleService : GenericLookupModelDataService<tblkp_Role, RoleViewModel>
{ }
public abstract class GenericLookupModelDataService<TModel, TViewModel> : object
where TModel : GenericLookupModel, new()
where TViewModel : GenericLookupViewModel, new()
{ }
public abstract class GenericLookupViewModel { }
public abstract class GenericLookupModel { }
public class RoleViewModel : GenericLookupViewModel { }
public partial class tblkp_Role : GenericLookupModel
{
}
public partial class tblkp_Role
{
public tblkp_Role()
{
}
}
This compiles successfully.
Therefore I suspect that compiler is unaware of the full definition of tblkp_Role.
I would suggest re building the library and re-referencing it again (Also check the reference path to ensure you are not mistakenly referencing older version).
I have faced similar issues with partial classes which are automatically created by EF in DB first approach specifically when I tried to define metadata classes.

Categories

Resources