Access multiple projects from IIncrementalGenerator - c#

I need to create code by the IIncrementalGenerator in at least two projects which are referring to the same library that references the SourceCodeGenerator project.
My solution, for further clarification:
MySolution
|->DesktopApp
| |->ref:Library
|
|->Library
| |->ref:SourceGenerator
|
|->WebApi
| |->ref:Library
|
|->SourceGenerator
In the library, I define Interfaces for all objects I use across my application, many properties I define in the interface are Ids which I mark by an attribute where I head the IType of the underlying object.
The attribute:
[System.AttributeUsage(System.AttributeTargets.Property)]
public class NavigationPropertyAttribute : System.Attribute
{
public System.Type NavigationPropertyType { get; }
public NavigationPropertyAttribute(System.Type type) => NavigationPropertyType = type;
}
This is how such an Interface looks like:
public interface IFoo
{
int Id { get; set; }
[NavigationProperty(typeof(IBar))]
int BarId { get; set; }
[NavigationProperty(typeof(IFoo))]
int ParentId { get; set; }
[NavigationProperty(typeof(IFoo))]
int ChildId { get; set; }
}
In both projects, one is a desktop application, the other is a Web API using EF Core, I have partial classes implementing these interfaces.
namespace IGTryout.Main;
public partial class Foo : IFoo
{
public int Id { get; set; }
public int BarId { get; set; }
public int ParentId { get; set; }
public int ChildId { get; set; }
}
what I need now, and where I'm struggling with, is using the IIncrementalGenerator to create a partial class with properties based on the Id, Name and the Type from the NavigationPropertyAttribute.
public partial class Foo
{
private Bar bar;
public Bar Bar => bar ??= GetValue<Bar>();
private Foo parent;
public Foo Parent => parent ??= GetValue<Foo>();
private Foo child;
public Foo Child => child ??= GetValue<Foo>();
}
(GetValue<T>() is an extension which gets the [CallerMemberName], resolves the matching Id via reflection and returns the object from cache by resolving it by the type and id)
In the Web API project, I'm also implementing the interfaces, yet, creating the NavigationProperties the common way
Foo { get; set; }
to fullfill EF Core's needs, also I'm creating the InversePropertyCollection in the related object for having the foreign keys set as needed by EF Core.
All of this is done, so I can use the interfaces as TransportObject with as little overhead as possible when sending them from the Web API to my desktop application and reverse.
Now to the problem I have:
to trigger all changes done to the interfaces, I reference my SourceCodeGenerator project from the library project, but by doing so, I can not find any opportunity to create the code inside the Web API assembly or the desktop app assembly.
I did solve this problem before by using the common SourceGenerator and referencing the SourceCodeGenerator project by both, the desktop app and the Web API, and accessing the interfaces inside my library project by calling the ReferencedAssemblySymbols of my compilations SourceModule and iterating over to the relevant project, since it is referenced by both, but this rather hacky solution is not possible, since would loose all the possibilities, which led me to switch to the IIncrementalGenerator in the first place.

Below is the answer to why this will not work with a Class Library project. however while tying up this answer I learned of the existence of Shared Projects which based on a cursory look at how they work, might enable the behavior of incremental generators on shared source code, see thoughts below.
The Problem
There are at 2 distinct reasons why what is asked in the question won't work with a Class Library project. One is to use source generator in a way they are not designed to work, and two, even if they worked as you wanted, the generated code would not behave as expected.
Source Generators hook to a compilation
By design, Source Generators hook to a compilation. The purpose of projects is to provide a distinct compilation unit. The use of solutions to group projects and order projects is a convivence, but does not cause the individual projects to merge into one compilation. Once a class library project is visible to the downstream projects, it is not longer a bunch of source code, it is just a dll.
Partial Classes in Different Projects
The example is making a partial class with the partials in three different projects, so even if you got the source generator to make the files in the correct project you would not end up with what you wanted, which is a WebApp version of Foo and and Desktop version of Foo. Instead you would end up with 3 versions of Foo the Library version with the Id Properties, the WebApp version with the auto properties for the navigations, and the Desktop version with the GetValue call. with both the desktop and webapp version missing the Id Properties. see this question/answer
Shared Projects
Can Shared Projects help here? Maybe. The intent of shared projects is to shared source code, not to be a compilation unit, so a change to the source should trigger the incremental generator in any project referencing that shared source, since that source is now part of that project's compilation.
You will want to be aware of the differences between a class library, and a shared project, and how that can impact your program, as now that code is being compiled multiple times in different contexts, so you can actually end up with completely different functionality from the same source. (i.e. features like global usings could cause namespace resolution to cause a Type to match by name to one in a completely different assembly, e.g. you have a property Document {get;set;} which in one project resolves to say AutoCAD.Document, but in another resolves to Microsoft.CodeAnalysis.Document.)

Related

How to share data between Service Project (WCF) and another project

(Edited) -> I have a solution with different projects. It has a strange structure because I'm not an professional software engineer. The solution has three parts: one HMI project (WPF), bussiness project (class library type) and service project (WCF).
In the bussiness project, I do some process that has result which I save in a static class. This results are exposed in the screen by WPF project mentioned previously and now I need to transfer this data by one service placed in the WCF project.
The object is similar to:
[DataContract]
public class ObjectToTransfer
{
[DataMember]
public string ParameterOne = string.Empty;
[DataMember]
public string ParameterTwo = string.Empty;
}
In the other hand, the method that picks up the informtion from static class belonging to bussiness project is like this:
[ServiceBehavior]
public class Service: IService
{
public ObjectToTransfer SendObject()
{
return new ObjectToTransfer{
ParameterOne = BusinessProject.StaticClass.ResultOne,
ParameterTwo = BusinessProject.StaticClass.ResultTwo
};
}
Well, when I consume the service the result is a object with the parameters one and two empty. In additon, when I debug the solution with both projects run, the static class hasn't info in her atributes... It's like whether both projects run independently, in spite of both are under the same solutions..., It's like whether there was no relationship between them. Where is the mistake?
Thank you in advance!
The main thing you will need to do is change the DataContract class to a class with proper getters and setters.
[DataContract]
public class ObjectToTransfer
{
[DataMember]
public string ParameterOne { get; set; }
[DataMember]
public string ParameterTwo { get; set; }
}
The reason this is important is because of the way WCF works when it try to serialize the data from the server to the client. Essentially, the class on the server side schematically matches a generated class on the client. (They share the same class schema.) If there is no proper structure to the class, the data cannot get set. The members are basically read-only.
What you have in your class right now are public member variables, and they are set to always return string.Empty. WCF can't work with that, it is too strict. The server and the client need to be able to read and write to all DataMember properties, so you need the { get; set; }.
Don't forget to refresh your service reference after making this change.
If you need some good resources to learn WCF, check out these videos WCF Tutorial. They are a little dated, but all of the concepts still apply.
This might be late and I am new to WCF service, so I am sharing based on what I have done in my case to share data among different projects.
Background of my project:
I have a WCF service(host), a client and a server project. (A total of 3 different projects.) I will perform the necessary calculation in WCF service. Afterwards, I am suppose to share the result to the client and server. Therefore, there is this variable, variableA, that I have share among the 3 projects.
What I did:
In WCF service, I declare variableA and mark it static:
private **static** string variableA;
Only after I marked variableA as static, then I can see the same variableA across different projects.
I am not sure if this helps but this is what I did in my case.

circular dependency In EF

The project is a modular form. Each of the modules are located in a separate Laibary. But sometimes it is necessary to communicate between modules. Example, to create a relationship in EF.
But when I do it well, then add reference to project A to B is typically not a problem.
But when I want to add a project reference to B to A, I encounter the following error.
a refrence to could not be added . adding this prpject as a refrence would cause a circular dependency
Each module is a project of the h code., And each module is located in a project.
Each module has its own Domain field., And its attributes are defined.
(ORM) using the EF.
Sometimes they come in contact with the module.
For example, a module as news Comments that wish to communicate with each other. A few have a relationship.
But to insert a reference to this relationship Letting my other projects. I encounter this error
News modula
public class News
{
public string Title { get; set; }
public string Text { get; set; }
public IList<Comment> Comments { set; get; }
}
Comment modula
public class Comment
{
public string Title { get; set; }
public string TextComment { get; set; }
public News New { set; get; }
}
I appreciate if you can help me better design project
I see two options here:
Move shared code to third library, so that A and B will reference it, but not each other
Merge A and B into one library
You're linking both entities together as it is recommended in pretty much every Entity Framework tutorial I have seen. However, I would only do it this way in small projects where you can put all the domain model in one assembly.
If you are working on a bigger project, where you need to separate the domain model in different assemblies according to the subdomains, I wouldn't link both entities together. Instead I would ask myself, is the news responsible for the data of its comments, or are the comments responsible for the data of the news?
This way you can find the root entity, wich could be either of them depending on the context. For example, if you decide that the news is the root, then the news would contain comments, but the comments should only reference the news Id, as they are not responsible for the news data.

Refactoring duplicate code when the only difference is the type of one variable?

I have to be able to connect to two different versions of the an API (1.4 and 1.5), lets call it the Foo API. And my code that connects to the API and processes the results is substantially duplicated - the only difference is the data types returned from the two APIs. How can I refactor this to remove duplication?
In Foo14Connector.cs (my own class that calls the 1.4 API)
public class Foo14Connector
{
public void GetAllCustomers()
{
var _foo = new Foo14WebReference.FooService();
Foo14WebReference.customerEntity[] customers = _foo.getCustomerList;
foreach (Foo14WebReference.customerEntity customer in customers)
{
GetSingleCustomer(customer);
}
}
public void GetSingleCustomer(Foo14WebReference.customerEntity customer)
{
var id = customer.foo_id;
// etc
}
}
And in the almost exact duplicate class Foo15Connector.cs (my own class that calls the 1.5 API)
public class Foo15Connector
{
public void GetAllCustomers()
{
var _foo = new Foo15WebReference.FooService();
Foo15WebReference.customerEntity[] customers = _foo.getCustomerList;
foreach (Foo15WebReference.customerEntity customer in customers)
{
GetSingleCustomer(customer);
}
}
public void GetSingleCustomer(Foo15WebReference.customerEntity customer)
{
var id = customer.foo_id;
// etc
}
}
Note that I have to have two different connectors because one single method call (out of hundreds) on the API has a new parameter in 1.5.
Both classes Foo14WebReference.customerEntity and Foo15WebReference.customerEntity have identical properties.
If the connectors are in different projects, this is an easy situation to solve:
Add a new class file, call it ConnectorCommon and copy all of the common code, but with the namespaces removed. Make this class a partial class and rename the class (not the file) to something like Connector.
You will need to add a link to this to each project.
Next, remove all of the code from your current connector classes, rename the class (not necessarily the file) to the same as the partial class, and add a using statement that references the namespace.
This should get what you are looking for.
So, when you are done you will have:
File ConnectorCommon:
public partial class Connector
{
public void GetAllCustomers()
{
var _foo = new FooService();
customerEntity[] customers = _foo.getCustomerList;
foreach (customerEntity customer in customers)
{
GetSingleCustomer(customer);
}
}
public void GetSingleCustomer(customerEntity customer)
{
var id = customer.foo_id;
// etc
}
}
File Magento15Connector
using Foo15WebReference;
partial class Connector
{
}
File Magento14Connector
using Foo14WebReference;
partial class Connector
{
}
Update
This process can be a little confusing at first.
To clarify, you are sharing source code in a common file between two projects.
The actual classes are the specific classes with the namespaces in each project. You use the partial keyword to cause the common file to be combined with the actual project file (i.e. Magneto14) in each project to create the full class within that project at compile time.
The trickiest part is adding the common file to both projects.
To do this, select the Add Existing Item... menu in the second project, navigate to the common file and click the right-arrow next to the Add button.
From the dropdown menu, select Add as link. This will add a reference to the file to the second project. The source code will be included in both projects and any changes to the common file will be automatically available in both projects.
Update 2
I sometimes forget how easy VB makes tasks like this, since that is my ordinary programming environment.
In order to make this work in C#, there is one more trick that has to be employed: Conditional compilation symbols. It makes the start of the common code a little more verbose than I would like, but it still ensures that you can work with a single set of common code.
To employ this trick, add a conditional compilation symbol to each project (ensure that it is set for All Configurations). For example, in the Magento14 project, add Ver14 and in the Magento15 project add Ver15.
Then in the common file, replace the namespace with a structure similar to the following:
#if Ver14
using Magneto14;
namespace Magento14Project
#elif Ver15
using Magneto15;
namespace Magento15Project
#endif
This will ensure that the proper namespace and usings are included based on the project the common code is being compiled into.
Note that all common using statements should be retained in the common file (i.e., enough to get it to compile).
If the FooConnectors are not sealed and you are in control to create new instances, then you can derive your own connectors and implement interfaces at the same time. In c# you can implement members by simply inheriting them from a base class!
public IFooConnector {
void GetAllCustomers();
}
public MyFoo14Connector : Foo14Connector, IFooConnector
{
// No need to put any code in here!
}
and then
IFooConnector connector = new MyFoo14Connector();
connector.GetAllCustomers();
You should introduce an interface that is common to both of the implementations. If the projects are written in the same language and are in different projects, you can introduce a common project that both projects reference. You are then making a move towards having dependencies only on your interface which should allow you to swap in different implementations behind the scenes somewhere using inversion of control (google, dependency injection or service locator or factory pattern).
Difficulties for you could be:
1) Public static methods in the implementations are not able to be exposed staticly via an interface
2) Potentially have code in one implementation class ie Foo14Connector or Foo15Connector that doesnt make sense to put into a generic interface

C# OOP File Structure?

I just started programming with objects recently and am trying to learn good habits early on.
The way I plan to structure my application is to have two files:
1: Program.cs - This file will contain the main logic for the application
2: Class.cs - This file will contain all of the class definitions
Pretty simple. What I'm wondering if I should have any more files for ... well, you tell me.
Any help would be appreciated.
It's generally accepted that each Class should have it's own file.
Program.cs - This file will contain
the main logic for the application
I am assuming when you say this that you mean that the main class is in this file. (The class with the entry point to the application). The various parts of the logic should be separated out and placed in the classes that make the most sense to have them.
Links to object oriented design:
http://www.csharphelp.com/2006/05/designing-object-oriented-programs-in-c/
http://www.informit.com/articles/article.aspx?p=101373
Links to namespaces:
http://www.csharphelp.com/2006/02/namespaces-in-c/
http://msdn.microsoft.com/en-us/library/dfb3cx8s.aspx
Here are some basics to help you get started. =)
.Net Naming Conventions and Programming Standards and Best Practices;
Object-Oriented Concepts;
Object-oriented design;
C# Coding Style Guide;
File Organization
Code Convention C#;
Design Guidelines for Class Library Developers;
The architecture of your solution might look like this:
One project for your classes (One class per file);
One project for your data access;
One project for your GUI;
One project for your integration layer (Such as NHibernate, EntityFramework, etc.)
Bear in mind that you must make each piece of code as reusable as possible. Doing so by writing your business objects (your classes) into an independant project will allow you to reference this project into another one later on, so you won't have to recode all of your business logic (methods, etc.) and your business objects (classes, enumerations, interfaces, etc.)
The object-oriented design is trying to generalize every practical aspect of an object and bringing it to the top most general class for your business objects. For instance:
// File: Person.cs
public class Person {
public string Name { get; set; }
public string Number { get; set; }
// Some other general properties...
}
// File: Customer.cs
public class Customer : Person {
public Customer() {
Orders = new List<Order>();
}
public string CreditTerm { get; set; }
public IList<Order> Orders { get; }
}
// File: Contact.cs
public class Contact : Person {
public long PhoneNumber { get; set; }
public long FaxNumber { get; set; }
}
// File: Supplier.cs
public class Supplier : Person {
public Supplier() {
Salesperson = new Contact();
}
public Contact Salesperson { get; }
}
It is also recommended to specify what each of your projects stand for. Let's take for instance an application for Customer Management:
MyCompany.MyCustomerMgmtSoftware.Domain <= This project shall contain your business classes definitions
MyCompany.MyCustomerMgmtSoftware.Data <= This project shall contain classes for data accessing your DBRM.
MyCompany.MyCustomerMgmtSoftware <= This project normally contain your GUI
MyCompany.MyCustomerMgmtsoftware.Mappings <= This project should contain your mapping files (while using NHibernate, for instance.
Does this help?
My only suggestion would be to break each class in Class.cs into its own file named ClassName.cs.
It'll make finding and fixing bugs easier down the road.
Less code in each file = less searching to find the offending code.
Each class should have its own file - not one .cs file containing many classes. I'm not sure, not having tried it, but your IDE may enforce this.
The generally accepted principal to follow is to have one file for each class (or partial class, in the case of 2.0+ apps). For any non-trivial application, you certainly would not want all of your class definitions in a single file.

Dynamic class creation

we have a data-layer which contains classes generated by outputs (tables/views/procs/functions) from database. The tables in database are normalized and are designed similar to OOP design ( table for "invoice" has 1:1 relation to table for "document", table for "invoice-item" has 1:1 relation to table for "document-item", etc...". All access to/from databaes is by stored procedures (for simple tables too).
Typical clas looks like (shortly):
public class DocumentItem {
public Guid? ItemID { get; set; }
public Guid? IDDocument { get; set; }
public DateTime? LastChange { get; set; }
}
public class InvoiceItem : DocumentItem {
public Guid? IDProduct { get; set; }
public decimal? Price { get; set; }
}
The problem is, the database tables has relations similar to multiple inheritance in OOP. Now we do a new class for every database output. But every database outputs are combination of "pure" tables in database.
The ideal solution would be (IMHO) tranform classes to interface, use the multiple implementation of interfaces, and then automaticly implement the members (this "table-classes" has only properties, and body of properties are always same).
For example:
public interface IItem {
Guid? ItemID { get; set; }
DateTime? LastChange { get; set; }
}
public interface IDocumentItem : IItem {
Guid? IDDocument { get; set; }
}
public interface IItemWithProduct : IItem {
Guid? IDProduct { get; set; }
}
public interface IItemWithRank : IItem {
string Rank { get; set; }
}
public interface IItemWithPrice : IItem {
decimal? Price { get; set; }
}
// example of "final" item interface
public interface IStorageItem : IDocumentItem, IItemWithProduct, IItemWithRank { }
// example of "final" item interface
public interface IInvoiceItem : IDocumentItem, IItemWithProduct, IItemWithPrice { }
// the result should be a object of class which implements "IInvoiceItem"
object myInvoiceItem = SomeMagicClass.CreateClassFromInterface( typeof( IInvoiceItem ) );
The database contains hunderts of tables and the whole solution is composed from dynamicly loaded modules (100+ modules).
What do you think, is the best way, how to deal with it?
EDIT:
Using partial classes is good tip, bud in our solution can not be used, because "IDocumentItem" and "IItemWithPrice" (for example) are in different assemblies.
Now, if we make change in "DocumentItem" table, we must go and re-generate source code in all dependent assemblies. There is almost no reuse (because can not use multiple inheritance). Its quite time consuming, if there are dozens of dependent assemblies.
I think it is a bad idea to automatically generate your domain model from your database schema.
So, you're really looking for some kind of mix-in technology. Of course, I have to ask why you aren't using LINQ to Entity Framework or NHibernate. O/RMs handle these problems by mapping the relational model into usable data structures that have APIs to support all of the transactions that you'll need to manipulate data in the database. But I digress.
If you are really looking for a mix-in technology to do dynamic code generation, check out Cecil at the Mono Project. It's a way better place to start than trying to use Reflection.Emit to build dynamic classes. There are other dynamic code generators out there but you may want to start with Cecil since the documentation is pretty good.
If you wish to continue auto-generating from the database and want to model multiple inheritance, then I think you have the right idea: Alter the tool to spit out interfaces with multiple inheritance, plus X num implementations.
You indicated elsewhere that a convention for inheritance vs. aggregation is enforced, and (as I understand) you know exactly how the resulting interfaces and classes should look. I understand that business rules are implemented elsewhere (maybe in a business rules engine?), so regenerating the classes should not require changes to dependent code, unless you want to take advantage of those changes, or existing properties have been altered or removed.
But you won't be done. Your classes will still have id's of related entities. If you want to make things easier for client code, you should have references to related entities (not caring about the related entity's ID), like this:
public class Person{
public Guid? PersonID { get; set; }
public Person Parent { get; set; }
}
That would make things easier on the client. When you think about it, going from ID's to references is work you have to do anyway; it's better to do it once in the middle tier than to let the client do it N number of times. Plus this makes your code less database-dependent.
So above all else, I recommend writing an OO wrapper for the auto-generated classes. You would program against this OO wrapper for almost everything; let only the data access layer interact with the auto-generated classes. Sure, you can't reuse inheritance metadata in the database (specified via conventions, I assume?), but at least you won't be carving a new path.
By contrast, what you have now looks like an anemic data model or worse.
The scenario is unclear to me.
If the code is generated, you don't need any magic: add some metadata to your database objects (e.g. Extended Properties in SQL Server) that flags the "basic" interfaces, and modify your generating template/tool to consider the flags.
If the question is about multiple inheritance, you are out of luck with .Net.
If the code is generated, you may also take advantage of partial classes and methods (are you using .Net 3.5?) to produce code in different source files.
If you need to generate code at run-time there are many techniques, not least ORM tools.
Now may you be a bit more explicit of your design context?

Categories

Resources